Ten-Pin Bowling Score Calculator by Frame
This ten-pin bowling score calculator turns a complete frame-by-frame record into an official game total.
Run — free
Enter the pins knocked down on each roll, keeping strikes as one-roll frames and placing any bonus rolls inside the tenth frame. The result shows the points earned by each frame, the running score after every frame, and the final total. Invalid combinations are rejected instead of being silently corrected, making the calculator useful for score checks, software tests, league records, and learning how strike and spare bonuses work.
Enter all ten frames in the correct shape
Provide exactly ten frame arrays in playing order. For frames one through nine, record a strike as [10]. Record every non-strike frame with two rolls, such as [7, 3] for a spare or [7, 2] for an open frame. Zeros matter: a gutter ball is a real roll, so an open frame with nine pins followed by a miss is [9, 0], not a one-value frame. The tenth frame holds its own bonus rolls. A strike there needs two more rolls, for example [10, 8, 1], while a spare needs one more, such as [6, 4, 7]. An open tenth frame has only its ordinary two rolls. Each roll must be an integer from zero through ten. The calculator expects a completed game rather than a game still in progress, because strike and spare values cannot be finalized until their following rolls exist. Keeping the rolls grouped by frame makes the source record easy to compare with a paper scoresheet while still preserving the chronological roll sequence needed for bonus calculations.
Understand strikes, spares, and the tenth frame
An open frame scores the pins knocked down in that frame. A spare scores ten plus the pins from the next roll, even when that next roll belongs to the following frame. A strike scores ten plus the pins from the next two rolls, which can cross one or two frame boundaries. Consecutive strikes therefore compound: the first strike in a run receives the next two strikes as bonuses and scores thirty. The tenth frame is different only because the game must supply enough rolls to settle its own bonus. Those extra rolls do not become an eleventh frame; they are included in the tenth-frame score. Pin decks also reset after a strike. Consequently, after a tenth-frame strike, a second strike permits any value from zero through ten on the last bonus roll. If the second roll is less than ten, the second and third rolls share one rack and cannot exceed ten together. After a tenth-frame spare, the final bonus roll may independently knock down as many as ten pins. These checks prevent plausible-looking but impossible sequences from receiving a score.
Read the result and use validation failures
The response contains three aligned views of the game. total_score is the final score, from zero through the perfect-game maximum of three hundred. frame_scores lists the points credited to each individual frame after bonuses are applied. cumulative_scores lists the running total through each frame, matching the totals normally written across a bowling scoresheet. This separation helps explain why a strike frame may be worth more than the pins visibly recorded inside its own box. It also makes automated comparisons precise: an integration can identify the first frame where two scoring systems disagree instead of comparing only the final number. Invalid input produces an error rather than a partial score. Typical failures include providing fewer than ten frames, adding a second roll to a strike before the tenth, knocking down more than ten pins on one rack, omitting a required tenth-frame bonus, or adding a bonus after an open tenth frame. The API price is $0.002 per completed calculation, while the browser version runs locally for quick manual checks.
What you can do with it
Verify a league scoresheet
Recalculate every frame and compare both cumulative totals and the final score before recording a result.
Test scoring software
Use deterministic strike, spare, open-frame, and tenth-frame cases as regression fixtures for a bowling application.
Teach official scoring
Show learners how the next one or two rolls change spare and strike frame values without hiding the arithmetic.
FAQ
How should I enter a strike?
Use a one-roll frame containing 10 in frames one through nine. In frame ten, include the two bonus rolls in the same frame array.
How should I enter a spare?
Use two rolls that total 10. A spare in frame ten also requires its single bonus roll as a third value.
Does the calculator support a perfect game?
Yes. Enter nine frames of [10], followed by [10, 10, 10] in frame ten; the official total is 300.
Why was my tenth frame rejected?
A strike requires two bonus rolls, a spare requires one, and an open frame permits none. Bonus rolls must also respect when the pin deck resets.
What does a calculation cost?
Each API calculation costs $0.002. The browser calculator can run the same deterministic logic locally.
Can I submit a game that is still in progress?
No. This capability requires all ten frames and every bonus roll needed to settle the official final score.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/game/bowling-score-calculate \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"frames":[[10],[7,3],[9,0],[10],[0,8],[8,2],[0,6],[10],[10],[10,8,1]]}'const res = await fetch("https://api.kit.forhosting.com/game/bowling-score-calculate", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"frames": [
[
10
],
[
7,
3
],
[
9,
0
],
[
10
],
[
0,
8
],
[
8,
2
],
[
0,
6
],
[
10
],
[
10
],
[
10,
8,
1
]
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/game/bowling-score-calculate",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"frames": [
[
10
],
[
7,
3
],
[
9,
0
],
[
10
],
[
0,
8
],
[
8,
2
],
[
0,
6
],
[
10
],
[
10
],
[
10,
8,
1
]
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/game/bowling-score-calculate", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"frames":[[10],[7,3],[9,0],[10],[0,8],[8,2],[0,6],[10],[10],[10,8,1]]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"frames":[[10],[7,3],[9,0],[10],[0,8],[8,2],[0,6],[10],[10],[10,8,1]]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/game/bowling-score-calculate", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"frames": [
[
10
],
[
7,
3
],
[
9,
0
],
[
10
],
[
0,
8
],
[
8,
2
],
[
0,
6
],
[
10
],
[
10
],
[
10,
8,
1
]
]
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "game.bowling_score_calculate",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |