Validate Sudoku Board Legality
A Sudoku position can look plausible while already containing a contradiction that makes any later solving effort pointless.
Run — free
This validator checks a partially filled 9x9 board without attempting to solve it. It examines every completed cell against the standard uniqueness rules for rows, columns, and 3x3 boxes, then returns a clear legality result and the exact locations of any duplicated digits. Blank cells are ignored, so you can check an opening puzzle, a work in progress, or a nearly completed grid.
Enter a partial Sudoku board in a dependable format
Provide exactly nine lines, with exactly nine cells on each line. Filled cells use the digits 1 through 9. Represent an empty cell with either a period or zero. Compact rows such as <code>53..7....</code> are convenient for copying puzzles from plain text, while spaces or commas may be inserted between cells when readability matters. The validator removes those separators before counting cells, but it does not guess what other symbols mean. This strict boundary is useful because a malformed grid is different from an illegal Sudoku position: malformed input cannot reliably identify 81 cells, whereas an illegal position has a valid shape but breaks one or more uniqueness rules. If the text has too many or too few rows, if any row has the wrong number of cells, or if a character is not a supported digit or blank marker, the request returns an input error. No assumption is made about whether the puzzle has a solution, whether clues are symmetric, or whether the number of clues is sufficient. The input describes the current board only.
Understand how row, column, and box conflicts are reported
The check applies the three standard local legality rules independently. A filled digit may occur no more than once in each horizontal row, no more than once in each vertical column, and no more than once in each 3x3 box. Blanks do not participate in comparisons. When every filled cell satisfies all three conditions, the result sets <code>valid</code> to true and returns an empty violations list. When a duplicate exists, each violation names its kind, its one-based index, the repeated value, and every cell containing that value in the affected unit. Row and column coordinates are also one-based, matching the way people normally describe Sudoku squares. A pair of equal digits can legitimately create more than one report. For example, two fives in the same row and the same upper-left box violate both constraints, so both facts appear rather than one hiding the other. Reports are ordered consistently: rows first, then columns, then boxes; within each unit, digits appear from 1 to 9. That deterministic ordering makes results straightforward to test, compare, store, and present.
Use legality checking at the right point in a Sudoku workflow
Legality validation is a focused diagnostic, not a solver and not a proof that a puzzle is well designed. A result of true means only that the digits currently written do not duplicate another filled digit in their row, column, or box. The board may still have no possible completion because of a deeper contradiction, may admit several solutions, or may contain a clue arrangement that is unsuitable for publication. This distinction makes the tool especially useful as an early guard. A puzzle editor can call it after each change to highlight immediate mistakes. A data import can reject damaged or mistyped grids before sending them to a more expensive solver. A learning application can check a student's current entries without revealing a solution. Because the algorithm scans a fixed 81-cell board and performs no network requests, its behavior is quick and reproducible. The browser version can be used interactively, while automated systems can submit the same board through the API for $0.002 per request. In either setting, treat malformed input separately from a well-formed board whose <code>valid</code> result is false.
What you can do with it
Catch data-entry mistakes
Check a transcribed Sudoku grid for repeated clues before saving it or passing it to a solver.
Give focused player feedback
Identify the rows, columns, and boxes containing immediate conflicts without exposing the puzzle's solution.
Validate imported puzzle records
Reject malformed grids as input errors and flag well-formed but illegal positions in a predictable structured result.
FAQ
Does a valid result mean the Sudoku can be solved?
No. It means only that the currently filled cells do not violate row, column, or 3x3-box uniqueness. It does not test solvability or uniqueness of the final solution.
How should blank cells be written?
Use a period or the digit zero. Both are treated as empty and ignored during duplicate checks.
Can cells be separated by spaces or commas?
Yes. Spaces, commas, and vertical bars are accepted as visual separators. After separators are removed, each row must contain exactly nine cells.
Why can the same pair of cells appear in two violations?
A duplicate can break more than one rule at once, such as belonging to both the same row and the same 3x3 box. Each violated unit is reported independently.
What does the API request cost?
Each API request costs $0.002. The interactive browser version runs locally for free.
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/sudoku-validate-board \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"board":"53..7....\n6..195...\n.98....6.\n8...6...3\n4..8.3..1\n7...2...6\n.6....28.\n...419..5\n....8..79"}'const res = await fetch("https://api.kit.forhosting.com/game/sudoku-validate-board", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"board": "53..7....\n6..195...\n.98....6.\n8...6...3\n4..8.3..1\n7...2...6\n.6....28.\n...419..5\n....8..79"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/game/sudoku-validate-board",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"board": "53..7....\n6..195...\n.98....6.\n8...6...3\n4..8.3..1\n7...2...6\n.6....28.\n...419..5\n....8..79"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/game/sudoku-validate-board", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"board":"53..7....\\n6..195...\\n.98....6.\\n8...6...3\\n4..8.3..1\\n7...2...6\\n.6....28.\\n...419..5\\n....8..79"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"board":"53..7....\n6..195...\n.98....6.\n8...6...3\n4..8.3..1\n7...2...6\n.6....28.\n...419..5\n....8..79"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/game/sudoku-validate-board", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"board": "53..7....\n6..195...\n.98....6.\n8...6...3\n4..8.3..1\n7...2...6\n.6....28.\n...419..5\n....8..79"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "game.sudoku_validate_board",
"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. |