Roof batten calculator
A 2,000 sq ft roof plane with a 40 ft eave and battens at 16-inch centres takes 38 rows, 1,520 linear feet before waste. Enter the area, the eave width and the gauge to get the rows and the length to order. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Roof area
- 2,000
- Roof width
- 40
- Batten spacing
- 1.333333
- Waste percent
- 10
- Slope length
- 50
- Batten rows
- 38
- Spacing count
- 37
- Covered slope
- 49.333321
Tile roofs and many metal systems hang on horizontal battens (or purlins) set at a manufacturer gauge from eaves to ridge. Guessing how many courses and how many linear feet to buy is how jobs run short mid-slope or leave a trailer of unused timber. This roof batten calculator turns three practical numbers into a clear takeoff: the roof surface area, the eave width of the plane (the length of each batten course), and the center-to-center batten spacing along the slope. It derives slope length as area divided by width, counts inclusive rows with the classic floor of slope over spacing plus one, multiplies rows by width for total linear feet, and optionally scales that length by a waste percent for ordering. Keep every dimension in one unit system—typically square feet with feet for US linear-feet language—and treat spacing as the product gauge in those same linear units (for example 1.333 feet for sixteen-inch centers).
From roof area to batten rows
Tile roofs and many metal systems hang on horizontal battens set at the manufacturer's gauge from eaves to ridge. The calculator models one rectangular roof plane. It first finds the slope length by dividing the roof area by the eave width, so 2,000 sq ft over a 40 ft eave means a 50 ft slope. It then places a batten at the eaves and one every gauge up the slope, including a last course near the ridge when the step still fits, which is the number of whole spacings plus one. Each course runs the full eave width, so rows times width gives the total linear feet, and an optional waste percent scales that length for ordering.
Mistakes that leave a job short
Guessing courses and linear feet is how jobs run short mid-slope or leave a trailer of unused timber. The most common slip is mixing units: area in square feet and width in feet need the gauge in feet too, so 16 inches becomes 1.333333. The second is forgetting the batten at the eaves, which is why the count is always one more than the whole spacings. Take the gauge from the tile or panel data sheet rather than from memory, and work out each roof plane on its own when the roof has more than one.
What you can do with it
DIY tile roof batten shopping list
Measure surface area and eave width, enter the tile manufacturer’s gauge as spacing, set a waste percent, and order whole linear feet without shortfall mid-slope.
FAQ
What happens if roof area is zero or negative?
Roof area must be strictly positive; non-positive area is non-physical and would not define a valid row or linear-feet quantity.
Why is roof width required if I already know the area?
Area alone gives total surface but not how long each batten course is. Width is the eave length of each course; slope length is area ÷ width for a rectangular plane, which drives the row count.
What does it cost?
The browser tool is free and runs locally.
How do I enter a 16-inch batten gauge when the roof is in feet?
Enter it as 1.333333, because the gauge must use the same unit as the eave width. A 12-inch gauge is simply 1. Typing 16 would be read as 16 feet between battens.
Why does a 50 ft slope at 16-inch centres take 38 rows?
Fifty feet divided by 1.333333 gives 37 whole spacings once the remainder is dropped. Add the batten at the eaves and you get 38 rows, with a small gap left below the ridge.
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/home/roof-batten-count \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"roof_area":2000,"roof_width":40,"batten_spacing":1.333333}'const res = await fetch("https://api.kit.forhosting.com/home/roof-batten-count", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"roof_area": 2000,
"roof_width": 40,
"batten_spacing": 1.333333
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/home/roof-batten-count",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"roof_area": 2000,
"roof_width": 40,
"batten_spacing": 1.333333
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/home/roof-batten-count", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"roof_area":2000,"roof_width":40,"batten_spacing":1.333333}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"roof_area":2000,"roof_width":40,"batten_spacing":1.333333}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/home/roof-batten-count", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"roof_area": 2000,
"roof_width": 40,
"batten_spacing": 1.333333
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "home.roof_batten_count",
"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
The same pure function powers the free browser tool and the paid API, so a phone-side estimate matches a scripted purchase order later. Non-positive area, width, or spacing is rejected with invalid_input instead of a silent nonsense count.
How the rows and linear-feet formula works
With a fifty-foot slope and one-foot spacing you get fifty-one rows; with sixteen-inch centers (about one point three three three feet) the same slope yields thirty-eight rows after flooring the quotient and adding one. Waste percent must be zero or positive when supplied. Model one rectangular roof plane: battens run parallel to the eave, so each course is as long as roof_width, and courses step up the slope at a fixed center-to-center batten_spacing (the gauge printed on the tile or metal panel data sheet). Slope length along the rafter plane is roof_area divided by roof_width when area and width share one unit system—for example two thousand square feet with a forty-foot eave implies a fifty-foot slope length. Course count uses the inclusive-end rule taught on job sites and in estimating handbooks: place a batten at the eaves reference, then every spacing along the slope, including a course at or before the ridge when the last step still fits, which is exactly batten_rows equals floor of slope_length divided by batten_spacing plus one. Total net linear length is batten_rows times roof_width—the sum of every course. Optional waste_percent inflates that length to linear_feet_ordered so cuts, hip and valley damage, and damaged stock are covered without changing the geometric row count. The response also reports spacing_count (rows minus one), covered_slope (spacing_count times spacing), and ridge_gap (slope length minus covered_slope) so you can see whether the last gap is short of a full gauge and decide to tighten gauge or add a ridge course per manufacturer rules. The pipeline is deterministic floating-point arithmetic with no random seeds, no wall-clock timestamps, and no network calls, so identical inputs always produce identical JSON. Roof area, roof width, and batten spacing must each be strictly positive; zero or negative values are rejected with invalid_input because they are non-physical and would not define a purchase quantity.
What you send and what you get back
Classic check: two thousand square feet, forty-foot width, spacing one point three three three three three three feet (sixteen-inch centers), ten percent waste—slope length is fifty, rows are thirty-eight, linear feet is one thousand five hundred twenty, ordered length is one thousand six hundred seventy-two. Send three required finite numbers: roof_area for the surface you will batten in square units, roof_width for the eave width of the plane in linear units (each batten’s length), and batten_spacing for center-to-center gauge in the same linear unit. Optionally send waste_percent as a non-negative percent; when omitted it defaults to zero so linear_feet_ordered matches linear_feet. Missing fields, non-numeric strings, NaN, and Infinity fail with invalid_input and are not charged. When roof_area is zero or negative the request fails with a message that non-positive area is not allowed; the same positivity rule applies to roof_width and batten_spacing, and negative waste is rejected separately. On success the response echoes cleaned inputs including waste_percent, slope_length, integer batten_rows, spacing_count, covered_slope, ridge_gap, net linear_feet, linear_feet_ordered after waste, a unit_note reminding you to keep one unit system and restating the row formula, and a short formula string for documentation. Pricing for successful API calls is the published base of $0.002 per item; validation failures do not consume prepaid balance.
Where roof batten calculators show up in real work
Homeowners and owner-builders installing clay, concrete, or slate tiles need a trustworthy linear-feet and row count before ordering battens or booking delivery, especially when treated timber has long lead times. Metal-roof crews setting horizontal purlins or battens under standing-seam or exposed-fastener panels use the same geometry with the manufacturer’s on-center spacing converted into feet or metres. If you later need counter-batten verticals, hip and valley waste tables, underlayment rolls, tile counts, or metal panel counts, treat those as separate search intents and landings rather than overloading this roof batten linear-feet and rows calculator page. Roofing contractors build takeoffs from measured surface area and eave width; a single deterministic endpoint that applies floor-plus-one rows and an explicit waste percent reduces spreadsheet drift between the estimator and the purchasing clerk. Building-supply counters comparing two gauges for the same house can swap only batten_spacing while holding area and width fixed, then rank linear_feet and ordered length without re-deriving slope length each time. Property managers planning multi-building re-roofs feed a batch of areas and widths through the API and attach linear_feet_ordered to purchase orders so overstock stays intentional rather than accidental. Because this capability is a pure parse engine, API pricing is the published base of $0.002 per successful item and failed validation does not consume balance.
Integration use cases
Contractor batten takeoffs
Feed each plane’s area, width, and gauge into the same floor-plus-one formula so purchase orders match the estimate and ridge_gap stays auditable.
Gauge comparison at the counter
Hold roof area and width fixed, swap batten_spacing across candidate products, and compare batten_rows and linear_feet_ordered side by side.
Technical questions
How does this roof batten calculator work?
It divides roof_area by roof_width for slope length, sets batten_rows to floor(slope_length / batten_spacing) + 1, multiplies rows by width for linear feet, and scales by waste_percent for the ordered length.
How do I enter sixteen-inch centers?
Convert to the same unit as roof_width. Sixteen inches is 16/12 ≈ 1.333333 feet. If you work in metres, convert the manufacturer gauge to metres before calling the API.