Annual report
The State of TOEFL Practice 2026
Across 5,160 graded practice observations from 279 learners between March and August 2026, 88.5% practiced exactly one of the four TOEFL sections and 2.2% practiced all four. Where marks were lost differed by task: language use was weakest on Academic Discussion, format strongest on Write an Email. These are Reach120 practice figures, not ETS measurements.
This report answers one question — What does a cohort preparing for the 2026-format TOEFL actually practice, and where does it lose marks? — for the person who has to plan around the answer: a department head sizing a TOEFL programme, a programme director deciding what to timetable, a tutor deciding what to do on Tuesday. It states its method before its results, prints the sample size beside every number, and lists the findings it refused to publish alongside the ones it did.
What ETS states about the 2026-format test
The current Writing section lists 12 items: 10 Build a Sentence items, one Write an Email task, and one Academic Discussion task.
Reading and Listening items are machine-scored selected responses; the Write an Email, Write for an Academic Discussion, and all Speaking tasks are AI-scored constructed responses, per the specification’s item-type notes. Build a Sentence is also machine-scored, so “machine-scored” is not exclusive to Reading and Listening.
The 2026 specification lists the Reading section at 50 items and 35 raw points, with task types Complete the Words, Read in Daily Life, and Read an Academic Passage. The specification states its blueprint values are subject to revision.
Sourced from the ETS 2026 test specifications, read (link confirmed ). Everything outside this box is Reach120’s own reading of it.
Method
One population, defined before anything was counted
Every figure is computed over practice done on Reach120 by accounts that are not ours. Admin accounts, company-domain accounts, synthetic accounts and accounts with no identity are removed through the same database view the site’s live figures use, so a page and a published figure now disagree about nobody. A practice row with no account attached is dropped rather than counted: an unattributed row cannot be shown to be a learner’s.
A window closed at both ends, so the citation cannot drift
The window opens on the boundary of the current scoring regime — the date our own Writing scorer changed how it combines dimension scores into an overall — because pooling across that boundary would publish our engineering history as a fact about candidates. It closes on the read date. Both ends are fixed in the SQL, so re-running any query below returns the same rows a year from now as it did on the day this edition was written.
Three floors, and a cell that misses one shows nothing
A cell publishes only if it holds at least 30 observations, from at least 10 distinct learners, with no single learner holding more than a third of it. The third floor is the one people forget: thirty answers from three people clears a count-only gate and describes three people. A cell that misses any floor still appears, with its sample size and the floor it missed, because a reader who cannot see how small a cell is cannot tell “not measured” from “measured and not liked”.
Two kinds of number, never mixed
Reading and Listening figures are accuracy against an answer key — the item was right or it was not. Writing and Speaking figures are mean dimension scores on the 0–5 rubric scale our scorer stores. They are reported in their own units and never converted into a common one. No figure in this edition is a TOEFL band, and no figure is an ETS score.
The query is published, not described
Each block of figures carries the exact SQL that produced it, against the named production database, with the run date. Nothing is rounded twice: the value shown is the value the query returned. Anyone holding a comparable corpus can run the same shape against theirs and see whether their cohort behaves like this one.
The floors, stated once so a reader can tell what an absence means: a cell publishes only with at least 30 observations, from at least 10 distinct learners, and with no single learner holding more than one in 3 of them.
Findings, with the qualifier they are only true under
Every figure on this page is measured on Reach120 practice by a self-selected population, not on the TOEFL test. Reading and Listening figures are accuracy against an answer key; Writing and Speaking figures are mean scores from our own scorer on its own 0–5 rubric scale. Nothing here is an ETS score, an official band, or a statement about how anyone performed on the real test.
Measured . Every table carries the SQL that produced it.
The corpus this report is computed over
| Section | Graded observations | Learners |
|---|---|---|
| All four sections | 5,160 | 279 |
| Writing — scored responses | 3,474 | 233 |
| Reading — answered items | 1,082 | 39 |
| Speaking — scored attempts | 333 | 42 |
| Listening — answered items | 271 | 22 |
One observation is one graded thing: a scored Writing response, an answered Reading or Listening item, a scored Speaking attempt. The units are not comparable across sections, and the sections do not sum to a population — a learner who practiced two sections appears in both.
How many of the four sections one learner actually practices
The 2026 test is four sections and one overall result, but a self-directed learner does not distribute attention evenly across them. This is the share of learners in the corpus who touched one, two, three or all four sections at least once inside the window.
| Measure | Value | Observations | Learners |
|---|---|---|---|
| Practiced exactly one section | 88.5% | 279 | 279 |
| Practiced two sections | 4.7% | 279 | 279 |
| Practiced three sections | 4.7% | 279 | 279 |
| Practiced all four sections | 2.2% | 279 | 279 |
Share of the learners in this corpus. Not an estimate of any wider test-taking population.
The query behind this table
Run against Reach120 production Supabase project bnxvoguvwiutimpqgaxd on . Window: 2026-03-23 00:00:00 UTC (inclusive) to 2026-08-24 00:00:00 UTC (exclusive).
with internal as (select id::text as id from public.internal_accounts),
touch as (
select distinct user_id, 'Writing' s from writing_sessions
where status='scored' and task_type in ('WRITE_EMAIL','ACADEMIC_DISCUSSION','BUILD_SENTENCE','MOCK_TEST')
and user_id is not null and user_id not in (select id from internal)
and created_at >= timestamp '2026-03-23 00:00:00' and created_at < timestamp '2026-08-24 00:00:00'
union select distinct a.user_id, 'Reading' from reading_attempt_items i join reading_attempts a on a.id=i.attempt_id
where a.user_id is not null and a.user_id not in (select id from internal) and i.is_correct is not null
and a.started_at >= timestamptz '2026-03-23 00:00:00+00' and a.started_at < timestamptz '2026-08-24 00:00:00+00'
union select distinct a.user_id, 'Listening' from listening_attempt_items i join listening_attempts a on a.id=i.attempt_id
where a.user_id is not null and a.user_id not in (select id from internal) and i.is_correct is not null
and a.started_at >= timestamptz '2026-03-23 00:00:00+00' and a.started_at < timestamptz '2026-08-24 00:00:00+00'
union select distinct a.user_id::text, 'Speaking' from speaking_attempts a
where a.status::text='scored' and a.user_id is not null and a.user_id::text not in (select id from internal)
and a.created_at >= timestamptz '2026-03-23 00:00:00+00' and a.created_at < timestamptz '2026-08-24 00:00:00+00'
),
per as (select user_id, count(*) sections from touch group by 1)
select 'breadth '||sections as k, count(*) learners,
round(100.0*count(*)/(select count(*) from per),1) pct from per group by 1
union all
select 'reach '||s, count(*), round(100.0*count(*)/(select count(*) from per),1) from touch group by 1;Which section a self-directed learner picks
The same population, cut by section rather than by breadth. A learner appears in every section they practiced, so these shares sum above 100.
| Measure | Value | Observations | Learners |
|---|---|---|---|
| Practiced Writing | 83.5% | 279 | 279 |
| Practiced Speaking | 15.1% | 279 | 279 |
| Practiced Reading | 14.0% | 279 | 279 |
| Practiced Listening | 7.9% | 279 | 279 |
Share of the learners in this corpus. Not an estimate of any wider test-taking population.
The query behind this table
Run against Reach120 production Supabase project bnxvoguvwiutimpqgaxd on . Window: 2026-03-23 00:00:00 UTC (inclusive) to 2026-08-24 00:00:00 UTC (exclusive).
with internal as (select id::text as id from public.internal_accounts),
touch as (
select distinct user_id, 'Writing' s from writing_sessions
where status='scored' and task_type in ('WRITE_EMAIL','ACADEMIC_DISCUSSION','BUILD_SENTENCE','MOCK_TEST')
and user_id is not null and user_id not in (select id from internal)
and created_at >= timestamp '2026-03-23 00:00:00' and created_at < timestamp '2026-08-24 00:00:00'
union select distinct a.user_id, 'Reading' from reading_attempt_items i join reading_attempts a on a.id=i.attempt_id
where a.user_id is not null and a.user_id not in (select id from internal) and i.is_correct is not null
and a.started_at >= timestamptz '2026-03-23 00:00:00+00' and a.started_at < timestamptz '2026-08-24 00:00:00+00'
union select distinct a.user_id, 'Listening' from listening_attempt_items i join listening_attempts a on a.id=i.attempt_id
where a.user_id is not null and a.user_id not in (select id from internal) and i.is_correct is not null
and a.started_at >= timestamptz '2026-03-23 00:00:00+00' and a.started_at < timestamptz '2026-08-24 00:00:00+00'
union select distinct a.user_id::text, 'Speaking' from speaking_attempts a
where a.status::text='scored' and a.user_id is not null and a.user_id::text not in (select id from internal)
and a.created_at >= timestamptz '2026-03-23 00:00:00+00' and a.created_at < timestamptz '2026-08-24 00:00:00+00'
),
per as (select user_id, count(*) sections from touch group by 1)
select 'breadth '||sections as k, count(*) learners,
round(100.0*count(*)/(select count(*) from per),1) pct from per group by 1
union all
select 'reach '||s, count(*), round(100.0*count(*)/(select count(*) from per),1) from touch group by 1;Write an Email — mean score by rubric dimension
The scorer records four dimension scores on every response, each on the 0–5 rubric scale it stores. This is where a cohort loses marks on the shorter of the two open Writing tasks.
| Measure | Value | Observations | Learners |
|---|---|---|---|
| Language use | 3.04 | 785 | 100 |
| Organization | 3.16 | 800 | 115 |
| Content | 3.29 | 800 | 115 |
| Format | 3.60 | 785 | 100 |
Mean score on the 0–5 rubric scale the Reach120 scorer stores. A Reach120 practice estimate, never an ETS score.
The query behind this table
Run against Reach120 production Supabase project bnxvoguvwiutimpqgaxd on . Window: 2026-03-23 00:00:00 UTC (inclusive) to 2026-08-24 00:00:00 UTC (exclusive).
with internal as (select id::text as id from public.internal_accounts),
base as (select w.task_type, w.user_id, w.dimension_scores from writing_sessions w
where w.status='scored' and w.user_id is not null and w.user_id not in (select id from internal)
and w.created_at >= timestamp '2026-03-23 00:00:00' and w.created_at < timestamp '2026-08-24 00:00:00'
and w.task_type in ('WRITE_EMAIL','ACADEMIC_DISCUSSION')
and jsonb_typeof(w.dimension_scores->'rubric_score')='number'),
cells as (select task_type, d, user_id, (dimension_scores->>d)::numeric v
from base, unnest(array['content','organization','language_use','format']) d
where jsonb_typeof(dimension_scores->d)='number'),
per as (select task_type,d,user_id,count(*) c,avg(v) learner_mean from cells group by 1,2,3)
select task_type, d, sum(c) n, count(*) learners,
round(avg(learner_mean),2) learner_weighted_mean, max(c) top_learner
from per group by 1,2 order by 1, learner_weighted_mean asc;Write for an Academic Discussion — mean score by rubric dimension
The same four dimensions, the same scale, the same window — on the longer task. Read it against the table above rather than on its own: language use is the weakest dimension on both tasks, but the two strongest swap places, so a cohort that looks tidy on the short task is not necessarily tidy on the long one. Levels are NOT comparable between the two tables — the same scorer produced both, and its residual is published separately.
| Measure | Value | Observations | Learners |
|---|---|---|---|
| Language use | 2.60 | 579 | 73 |
| Organization | 2.93 | 589 | 88 |
| Format | 3.05 | 574 | 73 |
| Content | 3.26 | 589 | 88 |
Mean score on the 0–5 rubric scale the Reach120 scorer stores. A Reach120 practice estimate, never an ETS score.
The query behind this table
Run against Reach120 production Supabase project bnxvoguvwiutimpqgaxd on . Window: 2026-03-23 00:00:00 UTC (inclusive) to 2026-08-24 00:00:00 UTC (exclusive).
with internal as (select id::text as id from public.internal_accounts),
base as (select w.task_type, w.user_id, w.dimension_scores from writing_sessions w
where w.status='scored' and w.user_id is not null and w.user_id not in (select id from internal)
and w.created_at >= timestamp '2026-03-23 00:00:00' and w.created_at < timestamp '2026-08-24 00:00:00'
and w.task_type in ('WRITE_EMAIL','ACADEMIC_DISCUSSION')
and jsonb_typeof(w.dimension_scores->'rubric_score')='number'),
cells as (select task_type, d, user_id, (dimension_scores->>d)::numeric v
from base, unnest(array['content','organization','language_use','format']) d
where jsonb_typeof(dimension_scores->d)='number'),
per as (select task_type,d,user_id,count(*) c,avg(v) learner_mean from cells group by 1,2,3)
select task_type, d, sum(c) n, count(*) learners,
round(avg(learner_mean),2) learner_weighted_mean, max(c) top_learner
from per group by 1,2 order by 1, learner_weighted_mean asc;Reading — items answered correctly, by the skill the item tags
Reading items are checked against an answer key, so this is accuracy and not an estimate. Each item carries a skill tag, which makes the question a teachable one: not "how good is the cohort at Reading" but "which reading operation does it get wrong". Every rate is the mean of the per-learner rates, so one heavy practiser does not become a cohort.
| Measure | Value | Observations | Learners |
|---|---|---|---|
| Relationships between ideasWithheld: 21 observations, below the floor of 30. | withheld | 21 | 10 |
| Academic vocabularyWithheld: 22 observations, below the floor of 30. | withheld | 22 | 12 |
| Vocabulary in context | 64.7% | 420 | 32 |
| Completing a word from context | 67.5% | 173 | 28 |
| Word form in context | 67.8% | 111 | 31 |
| Main purpose | 78.0% | 52 | 21 |
| Inference | 78.3% | 105 | 25 |
| Main idea | 80.7% | 36 | 19 |
| Collocation in contextWithheld: 19 observations, below the floor of 30. | withheld | 19 | 9 |
| Factual detail | 91.5% | 123 | 23 |
Share of items answered correctly against the answer key, in Reach120 practice.
The query behind this table
Run against Reach120 production Supabase project bnxvoguvwiutimpqgaxd on . Window: 2026-03-23 00:00:00 UTC (inclusive) to 2026-08-24 00:00:00 UTC (exclusive).
with internal as (select id::text as id from public.internal_accounts),
ri as (select coalesce(i.skill_tag,'(untagged)') skill, a.user_id,
(case when i.is_correct then 1 else 0 end) c
from reading_attempt_items i join reading_attempts a on a.id=i.attempt_id
where a.user_id is not null and a.user_id not in (select id from internal) and i.is_correct is not null
and a.started_at >= timestamptz '2026-03-23 00:00:00+00'
and a.started_at < timestamptz '2026-08-24 00:00:00+00'),
per as (select skill,user_id,count(*) k,avg(c) learner_rate from ri group by 1,2)
select skill, sum(k) n, count(*) learners,
round(100.0*avg(learner_rate),1) learner_weighted_pct, max(k) top_learner
from per group by 1 order by learner_weighted_pct asc;Listening — items answered correctly, by the skill the item tags
The same cut on the smallest of the four corpora, and the place where the floors do the most work: most of the six tags cannot be published, some because the cell is too small and some because a single learner holds more than a third of it.
| Measure | Value | Observations | Learners |
|---|---|---|---|
| Organization of the talkWithheld: 8 observations, below the floor of 30. | withheld | 8 | 6 |
| Inference | 72.4% | 60 | 18 |
| Function of what was saidWithheld: one learner accounts for 19 of 56 observations, above the ceiling of one in 3. | withheld | 56 | 17 |
| Detail | 76.8% | 82 | 17 |
| Speaker attitudeWithheld: 18 observations, below the floor of 30. | withheld | 18 | 8 |
| GistWithheld: one learner accounts for 18 of 47 observations, above the ceiling of one in 3. | withheld | 47 | 15 |
Share of items answered correctly against the answer key, in Reach120 practice.
The query behind this table
Run against Reach120 production Supabase project bnxvoguvwiutimpqgaxd on . Window: 2026-03-23 00:00:00 UTC (inclusive) to 2026-08-24 00:00:00 UTC (exclusive).
with internal as (select id::text as id from public.internal_accounts),
li as (select coalesce(i.skill_tag,'(untagged)') skill, a.user_id,
(case when i.is_correct then 1 else 0 end) c
from listening_attempt_items i join listening_attempts a on a.id=i.attempt_id
where a.user_id is not null and a.user_id not in (select id from internal) and i.is_correct is not null
and a.started_at >= timestamptz '2026-03-23 00:00:00+00'
and a.started_at < timestamptz '2026-08-24 00:00:00+00'),
per as (select skill,user_id,count(*) k,avg(c) learner_rate from li group by 1,2)
select skill, sum(k) n, count(*) learners,
round(100.0*avg(learner_rate),1) learner_weighted_pct, max(k) top_learner
from per group by 1 order by learner_weighted_pct asc;Speaking — mean score by dimension, within each task
Listen and Repeat and Take an Interview are scored on different dimension sets, so the two blocks are read separately and never pooled. Take an Interview is much the smaller corpus and its four dimensions are reported without comment on their order, which a sample this size does not establish.
| Measure | Value | Observations | Learners |
|---|---|---|---|
| Listen and Repeat — intelligibility | 2.85 | 291 | 35 |
| Listen and Repeat — accuracy | 3.28 | 291 | 35 |
| Take an Interview — idea development | 3.31 | 42 | 18 |
| Take an Interview — fluency | 3.42 | 42 | 18 |
| Take an Interview — vocabulary | 3.53 | 42 | 18 |
| Take an Interview — grammar | 3.64 | 42 | 18 |
Mean score on the 0–5 rubric scale the Reach120 scorer stores. A Reach120 practice estimate, never an ETS score.
The query behind this table
Run against Reach120 production Supabase project bnxvoguvwiutimpqgaxd on . Window: 2026-03-23 00:00:00 UTC (inclusive) to 2026-08-24 00:00:00 UTC (exclusive).
with internal as (select id::text as id from public.internal_accounts),
b as (select a.user_id::text uid, (a.scores->>'taskType') task, k,
(a.scores->'dimensionScores'->>k)::numeric v
from speaking_attempts a, lateral jsonb_object_keys(a.scores->'dimensionScores') k
where a.status::text='scored' and a.user_id is not null
and a.user_id::text not in (select id from internal)
and jsonb_typeof(a.scores->'dimensionScores'->k)='number'
and a.created_at >= timestamptz '2026-03-23 00:00:00+00'
and a.created_at < timestamptz '2026-08-24 00:00:00+00'),
per as (select task,k,uid,count(*) c,avg(v) learner_mean from b group by 1,2,3)
select task, k, sum(c) n, count(*) learners,
round(avg(learner_mean),2) learner_weighted_mean, max(c) top_learner
from per group by 1,2 order by 1, learner_weighted_mean asc;Sittings that were started and then finished
A Reading or Listening sitting is a row from the moment it opens, so abandonment is visible here in a way it is not for Writing. The window closes a week early on purpose: a sitting opened two days before the read has not been abandoned, it is in progress, and counting it as a failure would manufacture the finding.
| Measure | Value | Observations | Learners |
|---|---|---|---|
| Reading sittings submitted | 59.1% | 113 | 27 |
| Listening sittings submittedWithheld: one learner accounts for 29 of 85 observations, above the ceiling of one in 3. | withheld | 85 | 14 |
Share of Reach120 practice sittings opened inside the window.
The query behind this table
Run against Reach120 production Supabase project bnxvoguvwiutimpqgaxd on . Window: 2026-03-23 00:00:00 UTC (inclusive) to 2026-08-24 00:00:00 UTC (exclusive).
with internal as (select id::text as id from public.internal_accounts),
u as (
select 'Reading' sec, user_id, status, started_at from reading_attempts
where user_id is not null and user_id not in (select id from internal)
union all
select 'Listening', user_id, status, started_at from listening_attempts
where user_id is not null and user_id not in (select id from internal)),
f as (select * from u where started_at is not null
and started_at >= timestamptz '2026-03-23 00:00:00+00'
and started_at < timestamptz '2026-08-17 00:00:00+00'),
per as (select sec, user_id, count(*) c,
avg(case when status='submitted' then 1 else 0 end) learner_rate from f group by 1,2)
select sec, sum(c) n, count(*) learners,
round(100.0*avg(learner_rate),1) learner_weighted_pct, max(c) top_learner
from per group by 1 order by 1;Every figure on this page is measured on Reach120 practice by a self-selected population, not on the TOEFL test. Reading and Listening figures are accuracy against an answer key; Writing and Speaking figures are mean scores from our own scorer on its own 0–5 rubric scale. Nothing here is an ETS score, an official band, or a statement about how anyone performed on the real test.
What this edition refused to publish
Which of the four sections is a cohort weakest in?
Refused. ETS’s own specification splits the test into machine-scored selected responses (Reading, Listening, Build a Sentence) and AI-scored constructed responses (Write an Email, Write for an Academic Discussion, Speaking), and our instruments mirror that split. A Reading accuracy percentage and a Writing rubric mean are not two readings of one ruler. Our own published research also measures the Writing estimate as compressed at both ends. Ranking the four would publish the difference between our instruments as a fact about candidates.
How does practice differ by country or first language?
Refused. No verified country or first language is stored on a practice row. The only country data on file is a Search Console count of countries a click came from, and a click is a visit, not a registered learner. Cutting this corpus by that would be a join between two populations that are not the same people.
How often is a Writing task started and then abandoned?
Not measurable here. A Writing row is written when the response is scored, so an abandoned attempt leaves no row to count. The completion figures in this edition are Reading and Listening only, where a sitting exists from the moment it opens. Reporting a Writing completion rate from this corpus would report 100% and mean nothing.
Do learners who practice here score better on the real test?
Not published, and not because it is inconvenient. That is an outcome claim, it needs a consented sample of reported official results, and the sample has not reached the publication floor. Until it does, the honest answer is that we do not know, and this report will not imply one from descriptive practice data.
Limitations
This is not a sample of TOEFL candidates
It is a sample of people who found one practice tool and chose what to do on it. They are self-selected, mostly arriving from search, and nothing here is weighted to any test-taking population. Treat every figure as a description of this corpus and not as an estimate of a national or global cohort.
The product shapes the behaviour being measured
The breadth finding is the clearest case. Reading and Listening practice is free and uncapped on this platform while AI-scored Writing is metered, so the mix of sections a learner touches is partly a fact about our pricing and not only about their intentions. A cohort inside a taught programme, told what to practice, would not look like this — which is arguably the point of reading it, but it is a limitation before it is a finding.
The Writing and Speaking figures come from our scorer, not from ETS
They describe how our model applies our reading of the rubric descriptors. Our own published research measures that scorer marking low levels high and the top level low, and the dimension means here inherit that. Comparisons WITHIN a table — one dimension against another, measured by one instrument on one corpus — are the part that survives that limitation. The absolute level is the part that does not.
The four corpora are wildly unequal in size
Writing is the bulk of the corpus and Listening is a fraction of it. Section-level findings are therefore not equally strong, and the Listening tables are where most of the withheld cells are. Read the sample size beside every number; it is printed for that reason and not as decoration.
Skill tags are ours, not ETS’s taxonomy
Each Reading and Listening item carries a skill tag applied when the item was authored. The tags are a Reach120 classification, they are not an ETS category system, and an item that tests two things carries one tag. Item difficulty is also not held constant across tags, so a low percentage may describe harder items rather than a weaker skill.
This edition describes one window and will not be edited
The figures are fixed to the window stated on each block. When the 2027 edition is measured it will be published as a new edition beside this one, not as a correction of it — a figure that has already been cited is a historical fact, and editing it in place erases the citation.
Citing this, and the next edition
Cite it as The State of TOEFL Practice 2026, Reach120, measured . The figures on this page are fixed to the window each table states and will not be edited: a number somebody has already cited is a historical fact, and correcting it in place erases the citation rather than fixing it. The next edition is published beside this one, not over it. Every figure lives in lib/research/state-of-toefl-2026-figures.ts with the query that produced it, so the arithmetic between the database and this page is checkable without our help.