Plate calculator
Type the weight you want on the bar and see exactly which plates go on each side: 100 kg on a 20 kg bar means 40 kg per side, one 25 and one 15. Heaviest plates first, and no rounding if the target cannot be built. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Target total (kg)
- 100
- Bar weight (kg)
- 20
- Plate weight per side (kg)
- 40
- Loaded total (kg)
- 100
barbell loading free
How a barbell is loaded, step by step
Loading a bar is simple arithmetic that is easy to get wrong in a busy gym. First take the weight of the empty bar away from the target; the standard bar weighs 20 kg. Then split what is left in half, because both sides must match. Finally fill each side starting with the heaviest plate that still fits, then the next one, until nothing is left. With the standard set of 25, 20, 15, 10, 5, 2.5 and 1.25 kg plates, a 100 kg target leaves 40 kg per side, which becomes one 25 kg plate and one 15 kg plate. The result also shows the exact total you end up lifting.
Common loading mistakes
The most frequent mistake is forgetting the bar, which makes every set heavier than planned. Another is loading the two sides differently, which tilts the bar. With standard plates, per-side loads move in steps of 1.25 kg, so some targets simply cannot be built; the calculator says so instead of rounding to a nearby weight. If your gym has a different bar or a different set of plates, enter them, because a loading that assumes plates you do not own is useless once you are under the bar. Values converted from pounds work too, as long as everything uses the same unit.
Reachability and the two special cases
The default plate set has a greatest common step of 1.25 kg, so per-side loads must be multiples of that step to be loadable. You can override plates entirely to model your own gym's inventory, including fractional or pound-converted values.
What you can do with it
Gym and training apps
Show a lifter exactly which plates to slide onto each side for a prescribed working weight, using their own bar.
Custom plate inventories
Pass a plates array to match a home gym's stock and detect when a target simply cannot be loaded.
FAQ
How does it decide which plates to use?
A greedy largest-first pass over the de-duplicated, descending-sorted plate set, loading as many of each size as fit per side.
How much weight goes on each side of the bar?
Subtract the bar from your target and split the rest in two. A 100 kg target on a 20 kg bar leaves 80 kg of plates, so each side carries 40 kg.
Why can my target weight not be loaded?
The plates you have cannot add up to the exact amount needed per side. With the standard set, each side must be a multiple of 1.25 kg; instead of giving you a close guess, the calculator tells you the target is out of reach.
Can I use my own plates or a different bar?
Yes. List the plates your gym actually has, including fractional ones or values converted from pounds, and enter the weight of your bar. The loading is then built only from what you own.
What if my target equals the empty bar or is lighter?
If the target equals the bar, you get no plates at all and the bar alone. If it is lighter than the empty bar, it cannot be loaded and the calculator refuses it.
Related tools
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/dev/plate-calculator \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"target_kg":100}'const res = await fetch("https://api.kit.forhosting.com/dev/plate-calculator", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"target_kg": 100
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/dev/plate-calculator",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"target_kg": 100
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/dev/plate-calculator", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"target_kg":100}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"target_kg":100}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/dev/plate-calculator", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"target_kg": 100
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "dev.plate_calculator",
"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. |
Technical notes
Given a target_kg, a bar_kg and an optional list of available plates, returns the largest-first plate loading for one side of the bar and the exact total achieved.
The greedy per-side algorithm
With the defaults (bar_kg 20 and the standard plate set 25, 20, 15, 10, 5, 2.5, 1.25) a target of 100 kg leaves 80 kg of load, 40 kg per side, and the greedy pass returns one 25 kg and one 15 kg plate per side, exactly the shipped example. Each entry in plates_per_side is a {weight, count} pair, sorted heaviest first, describing what to hang on one end of the bar.
Reachability and the two special cases
After the greedy pass, if any remaining weight above 1e-6 kg is left over, the load cannot be built from the given plates and the call throws invalid_input meaning the target is not reachable with the plate set, rather than returning an approximate loading. Two special cases short-circuit the algorithm: if the load equals zero (target equals bar) it returns per_side_kg 0, an empty plates_per_side array and total_kg equal to the bar; if the load is negative (target below the bar) it throws invalid_input because the target is lighter than the empty bar.
Inputs, validation and echoed totals
target_kg must be a finite number greater than zero, and bar_kg must be a finite number of at least zero, defaulting to 20 when omitted. The plates parameter, when supplied, must be a non-empty array in which every element is a finite number greater than zero; a non-array, an empty array or a non-positive plate each throws a typed invalid_input error. Note this capability performs no rounding of weights: per_side_kg is exactly load/2 and total_kg is recomputed as bar_kg + 2*per_side_kg, so the returned total reflects the true loaded weight rather than the requested target when they differ. The response echoes target_kg and bar_kg alongside per_side_kg, the plate breakdown and the total, giving a self-contained loading plan.
Integration use cases
Programming and coaching tools
Batch a whole session's target weights through the API to pre-render loading cues for each set.
Technical questions
What is the default bar and plate set?
A 20 kg bar and plates of 25, 20, 15, 10, 5, 2.5 and 1.25 kg, unless you override bar_kg or the plates array.
What if the target can't be built exactly?
If any weight remains after the greedy pass, it returns an invalid_input error meaning the target is not reachable with that plate set.
Are the results per side or total?
plates_per_side lists what goes on one end of the bar; per_side_kg is load/2 and total_kg is bar_kg plus twice that.
Is it free and what does the API cost?
The browser widget is free (tier A) and runs the identical deterministic core; API calls are billed per successful request from $0.002.