80 races, 614,738 finish times, one course
I lined up every Boston Marathon from 1940 to 2019 against the weather on the course that day. Below about 55°F the temperature makes no measurable difference. Above it, the median finisher loses 0.42% of their time per °F, which is 0.95 minutes at a 3:47 finish. The share is similar from the front of the race to the back, so slower runners lose more minutes.
of finish time per °F above 55°F for the median runner, 90% bootstrap interval 0.20 to 0.52 (16 races, 1998 to 2014).
the point where heat starts to cost time, picked by cross-validation from 40°F to 60°F. A straight line through all temperatures fits worse.
extra skill from dew point. On its own it predicted nothing, and added to temperature it made predictions worse.
average error on the held-out 2015 to 2019 races, with weather and without. The model got 2015 and 2016 right and 2018 badly wrong.
| Question | How much does race-day heat slow a marathon field? |
|---|---|
| Data | 80 Boston Marathons, 1940 to 2019, 614,738 finish times; ERA5 hourly weather at three course points |
| Method | One fit per position in the field: spline trend in year plus a slope per °F above 55°F; 1,000 bootstrap resamples of race years |
| Check | 2015 to 2019 held out: mean error 2.26% of finish time with weather against 2.15% for the five-year average with none; 33 of 45 actuals inside the 80% band |
| Result | Median finisher 0.42% per °F above 55°F (90% interval 0.20 to 0.52), 0.95 min at 3:47; all nine positions between 0.29% and 0.46% |
| Limitation | No term for cold rain, so 2018 was missed badly. The field percentiles rest on 16 races, two of them above 70°F |
Start: the question
The first version of this page, in June 2026, used fourteen Boston Marathons (2001 to 2014) and found that the median finish rose 1.03 minutes per °F (r = 0.86). That was a straight line through thirteen points, with gun times in some years and chip times in others, and nothing to separate the weather from a field that was growing and changing.
This version, from October 2026, uses 80 races, weather from a reanalysis at three points on the course, wind along the course, and a long-run trend for the field. It holds back the five most recent races (2015 to 2019), fits on the rest, and then checks whether the model predicts those five. It also ships the model, so you can price a race day from a live forecast.
Superseded, 28 October 2026: the June headline of 1.03 minutes per °F and "+31 minutes from 50°F to 80°F". The rate for the median runner is now 0.95 minutes per °F above 55°F, with no cost below it, so 50°F to 80°F is about 25 minutes. The section after the forecast tool explains why the number moved.
5 km: the data
Results come from adrian3/Boston-Marathon-Data-Project, one file per year from 1897 to 2019. The early files are short: before 1975 they list only the leading finishers (25 to 285 a year), and from 1975 to 1995 each list stops at a time limit (3:30 in 1976, 4:00 for most of the 1980s, about 5:00 in the early 1990s). A percentile of a list cut at a time limit moves with the cut, and on a hot day more runners fall past it. So the study uses three kinds of series, each only where the list supports it:
The repo's original source, llimllib/bostonmarathon,
covers 2001 to 2014. The two agree on finisher counts within 0.6% in every
year but 2013. Their medians match to 0.05 minutes in 2006 to 2011 and 2014.
In 2001 to 2005 and 2012 adrian3's median is 4.8 to 10.2 minutes faster,
because llimllib's official column in those years is gun time;
adrian3 matches llimllib's net column there to within 0.2 minutes.
This version uses adrian3 for every year. Wheelchair and handcycle entrants are
excluded (a W bib in llimllib; a two-hour floor catches any left in adrian3's
1993 and 1995 files).
Weather is the Open-Meteo historical archive
(ERA5 reanalysis, available from 1940), hourly, at the Hopkinton start,
Wellesley near halfway and the Boylston Street finish. The hours come from a
race table in tables/race_days.csv: April 19 through 1968 (the 20th
when the 19th was a Sunday), the third Monday of April since 1969, a noon start
through 2006 and 10:00 from 2007 with waves to 11:15. The front series use the
first two hours after the start; the others use the start to about four hours
after the last wave.
10 km: eighty years in one chart
Each point is one race: how far that race's time sat above or below the field's long-run trend, against the temperature on the course. The dashed line is the fitted model, flat to 55°F and rising after. 1976, at 87°F, is the hottest race in the data; 2012 (81°F) and 2004 (77°F) come next.
Men's 25th place
Hover or tab to a point for its year.
tables/series_*.csv.15 km: the confounder
Over eighty years the 25th man got 35 minutes faster (2:53:32 in 1940, 2:18:01 in 2019) and the field went from 55 listed finishers to more than 26,000, about 45% of them women. Any slope against temperature has to get past that. I fitted each series five ways: with no trend, a straight-line trend, era steps plus a line, a line plus field size and the share of women, and a smooth spline in the year (a restricted cubic spline with three to five knots). The spline is the main model.
| Series | No trend | Line | Eras | Field mix | Spline |
|---|
Heat slope in % of finish time per °F above 55°F, from
tables/trend_robustness.csv. At the front, ignoring the trend
halves the slope (0.24 against 0.41 for the 25th man), because hot races were
not spread evenly across the decades. For the whole-field percentiles the
choice barely matters: the median sits between 0.42 and 0.45 under every
choice. Eras do not apply there, since 1998 to 2014 has only one standards
change (2013) and one race after it.
21 km: temperature, dew point or heat index
Humid heat is supposed to be worse, so I expected dew point or a heat index to beat the thermometer. Each candidate was scored by leave-one-year-out cross-validation inside the training years, across all nine series, as the share of the trend-only model's error it removed.
| Weather term | Shape | Error removed |
|---|
Temperature with a bend at 55°F removed 37% of the error; a straight line in temperature removed 32%; the NWS heat index did about as well as temperature but not better. Dew point alone removed nothing. Added on top of temperature, every extra term (wind, dew point, humidity, rain, sun, cloud) made the cross-validated predictions worse. Boston's race days are mostly dry: the median race-day dew point is 37°F, and among the 16 whole-field training races only 2002, 2004 and 2012 went above 50°F. With so few humid races the data cannot separate humidity from heat.
25 km: who pays
Fitted separately for nine positions in the field, the heat slope in percent sits between 0.29% and 0.46% per °F. Converted to minutes it climbs from 0.43 per °F for the 10th man (2:13) to 0.95 at the median (3:47) and 1.34 at the 90th percentile (5:01). Switch the chart to minutes to see it.
Hover or tab to a point for its numbers.
tables/slopes_by_series.csv.30 km: the wind
The course heads east-northeast (a bearing of about 70°), so a west or southwest wind pushes runners to Boston. The chart shows the wind component along the course on every race day. 1994 and 2011 had tailwinds near 12 mph; 2018 had a 15 mph headwind with cold rain.
Hover or tab to a bar for its year.
For the 10th and 25th man, each mph of tailwind took 0.08% and 0.11% off the time (90% intervals 0.02 to 0.16 and 0.02 to 0.21). A 10 mph headwind costs a 2:20 runner about 1.3 minutes. For the whole-field percentiles the estimate came out with the wrong sign and an interval wide enough to include either answer, so the forecast tool applies the front-of-race rate, 0.095% per mph of headwind, to everyone and says so. Wind did not improve the cross-validated fit, so the main model leaves it out.
35 km: the holdout
2015 to 2019 were left out of every fit and every choice above. The level of the field kept drifting after 2014, so the fairest test is the weather part alone: does the model rank those five races in the right order and by the right amounts? The chart compares each race's predicted and actual time against the five-year average.
Hover or tab to a year.
tables/holdout_2015_2019.csv.Across the nine series and five years, the weather model's average error was 2.26% of finish time. Predicting every holdout race at the five-year average, with no weather at all, gave 2.15%. The model did well on cold, wet 2015 (0.90% against 2.76%) and warm 2016 (1.20% against 1.59%). It over-predicted 2017, a 69°F day with an 11 mph tailwind. It missed 2018 badly: 44°F, cold rain and a 15 mph headwind, a race the model scores as ideal, and the 10th man ran 7% slower than the five-year average. 33 of the 45 actual values fell inside the 80% bands (73%); in 2018 only one of nine did.
Holding the 2014 trend flat and predicting absolute times, the median finish was off by 6.3 minutes on average over the five races. The old June line, applied the same way, was off by 8.8. Both miss partly because the slower half of the field got faster after 2014: the 90th percentile in 2015 to 2017 was 6% to 7% below the 2014 trend, a year when the field was unusually large (31,809 finishers).
So the model prices heat. It has no term for cold rain, and on the races it never saw it did not beat a flat guess overall. Read the forecast below as the heat cost on a dry day, with the interval it shows.
40 km: your race
Enter the time you would run on a cool, still day. The tool asks Open-Meteo for the hourly forecast on race day, averages the race hours, and applies the model for your position in the field. More than 16 days out it falls back to climatology and says so. The next Boston Marathon is the default.
api.open-meteo.com for a forecast (dates from 60 days ago to 16 days ahead),archive-api.open-meteo.com for older dates, and for climatology away from Boston,geocoding-api.open-meteo.com for the town you type, only if you search.40.5 km: the June number
Three things moved the June result, and tables/old_vs_new.csv
keeps each step. First, the timing. llimllib's official column is
gun time in 2001 to 2005 and 2012, and gun time adds the minutes spent
reaching the start line, which grows with the field. That gap was 10.2
minutes at the median in hot 2004 and 4.8 minutes in hot 2012, so it made the
hot years look slower than they were. Re-running the June fit on the same 13
races with chip times and the course-wide weather window gives 0.76 minutes
per °F, not 1.03.
Second, the shape. A straight line spreads the heat cost over every degree, including the cool ones, where the 80 races show no effect. With the bend at 55°F, the slope above it is steeper than the straight line's, and the slope below it is zero. Third, the field: the new median series starts in 1998 instead of 2001 and has a trend in the year, so a slow year early on is not read as a hot one.
The net result for the median runner is 0.95 minutes per °F above 55°F. A 50°F race and an 80°F race are now about 25 minutes apart rather than 31, and a 50°F race and a 55°F race are the same.
41 km: limitations
The data describes Boston fields in April. A runner who trained through a hot summer, or a race in a humid place, may not follow it. The weather is a reanalysis on a 0.25-degree grid (about 20 by 28 km here), not a thermometer on the road, and one average over six hours hides a cool start and a warm finish.
The field percentiles rest on 16 races, only two of them above 70°F, so their intervals are wide and the 55°F bend was chosen across all nine series rather than for each. The bootstrap does not redo that choice, so the intervals are a little too narrow. Times before 2001 are as adrian3 publishes them; for the 2001 to 2005 and 2012 races they are chip times, and earlier ones are probably gun times.
Field size, qualifying standards, charity places and who chose not to start on a hot morning all move the numbers, and the trend only soaks up the slow part of that. In 2012 the B.A.A. let entrants defer to 2013 because of the heat forecast, so the 2012 field was not the field that entered.
Finish: how it was built
fetch_data.py downloads the results (adrian3 pinned to commit
cc4fdd8, plus llimllib for the cross-check) and the weather into a
cache outside the repo. build_dataset.py writes the per-year
tables, model_fit.py picks the weather terms, fits every series,
runs the bootstrap and the holdout and writes model/heat_model.json.
heat_model.py loads that file and is what
Marathon Strategy RAG
calls; the forecast tool on this page runs the same arithmetic in JavaScript.
legacy_2001_2014.py rebuilds the June result.
tables/race_days.csv