Skip to main content
20% off your first payment$5.99 for your first week, then $7.49 · Ends November 15, 2026See plans

For institutions, academies and tutoring platforms

Run your cohort against our prompts and our rubric score, inside your own product

If you run a prep programme, an academy, or a platform that tutors write on, this interface lets you pull TOEFL Writing practice prompts into your own assignment flow and post each learner’s text back for a band score against the same rubric the Reach120 app uses. Your learners stay in your product; the scoring happens here.

An individual developer building something small is welcome too — the same key works — but the limits, the version policy and the support path below are written for a programme with a roster and a term to get through.

How an integration is put together

  1. First. Pull prompts. Your assignment builder lists them, or you cache a set per term.
  2. Then. Your learner writes in your interface. Nothing is sent to us until they submit.
  3. Then. Post the text with the prompt id. You get a band score and a short rationale back, which you render in your own gradebook.

Everything is under /api/v1, and every response is JSON.

Authentication

  • Every request carries `Authorization: Bearer r120_live_…`. Keys begin `r120_live_` so one pasted into the wrong environment is visible to a human reading the paste.
  • We store a one-way hash of your key and never the key itself. It is shown once, at issuance, and cannot be recovered afterwards — if it is lost, it is replaced, not retrieved.
  • Revocation is immediate and per key, so a compromised integration is stopped without touching the rest of your programme. A revoked key answers exactly as an unknown one does.

Endpoints

GET /api/v1/practice-items

List Writing practice prompts.

Filter with `task_type` (write_email or academic_discussion), page with `limit` and `offset`. Default page size 20, maximum 100. Prompts only — no model answers are returned, and the Reading, Listening and Speaking banks are not exposed here because their rows carry answer keys.

GET /api/v1/practice-items/{id}

Fetch one prompt by id.

Ids are namespaced by task type, so an id can be posted straight back to the scoring endpoint without also naming the task.

POST /api/v1/score

Score one written response against the TOEFL Writing rubric.

Send `response` plus either `item_id` from the prompt endpoints or your own prompt inline. Responses are accepted up to 6000 characters and an inline prompt up to 4000. The reply carries the band score, a short rationale, and which tier scored it.

Limits

There are three, and they bound different things. Knowing which one you have reached is the difference between slowing down, waiting for tomorrow, and checking the status page.

Requests per minute, per key

Scope: Your key alone.

Default 60 per minute, set per key at issuance. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, so you can pace without hitting the wall to find it. A refused request does not count against the window.

Daily scoring-cost ceiling, per key

Scope: Your key alone.

Each key carries its own ceiling on how much scoring it can drive in a UTC day, set at issuance and named on your key record. Reaching it pauses scoring on that key until midnight UTC and affects no other key. The prompt endpoints keep working — they run no model.

Service-wide AI budget

Scope: Everyone, including us.

A monthly ceiling across the whole service, plus a switch an operator can use to pause all scoring during an incident. When either is in force, scoring refuses and says so. This is the limit that means one integration cannot take scoring down for everyone else.

When a limit is reached the request is refused with the code below. It is never quietly re-routed to a cheaper scorer and returned as though nothing happened — a score you were not told was produced differently is worse than no score.

Errors

Every refusal is { "error": { "code", "message" } }. Branch on the code, never on the sentence.

CodeHTTPWhat it means
API_KEY_MISSING401The request carried no Authorization: Bearer header, or the header was not a Reach120 key.
API_KEY_INVALID401The key is not recognised, or it has been revoked. A revoked key answers exactly as an unknown one does — the API will not confirm that a key it refuses ever existed.
RATE_LIMIT_EXCEEDED429This key has used its per-minute request allowance. Retry-After names the second the window reopens; a refused request does not count against the window.
KEY_SPEND_CEILING_REACHED429This key has reached its own daily ceiling on scoring cost. It resets at midnight UTC. Nothing else on your integration is affected, and no other key is.
AI_BUDGET_REACHED503Scoring is paused across the whole service — either the monthly AI budget is spent or an operator has paused it. Practice-item requests are unaffected.
LIMIT_CHECK_UNAVAILABLE503A limit could not be verified, so the request was refused rather than allowed. An allowance we cannot read is not an allowance.
INVALID_REQUEST400The body or query could not be acted on. The message names the field.
NOT_FOUND404No active practice item has that id.
SCORING_FAILED502The scorer did not return a usable result. The reservation this request made against your daily ceiling is refunded.

Versioning and deprecation

  • The version is in the path (`/api/v1`). Fields may be added to a response inside a version; a field is never removed or repurposed inside one.
  • A breaking change means a new version path, and the previous one keeps answering while integrators move.

Getting a key

Keys are issued by hand today, to a named contact at a named organisation. Write to partnerships@reach120.com with who you are, what you are building and roughly how much scoring a term involves, and you will get a key with limits set for that shape of use rather than a default.

We would rather say this plainly than put a self-serve button here that does not exist yet.

Notices

Reach120 is independent. The trademark and independence notice at the foot of this page applies to everything here, and the independence page sets out what that means in full. A score returned by this interface is practice feedback produced by our own scorer; it is not a result from the test maker and must not be presented to a learner as one.

How the scorer works, and what it is measured against, is documented on the methodology page.

Reach120 is an independent practice tool. It is not affiliated with, endorsed by, or approved by ETS, and it does not provide official TOEFL® test scores.

TOEFL® is a registered trademark of ETS. This product is not endorsed or approved by ETS.