Sudoku puzzle API
A free JSON endpoint that returns a fresh Sudoku puzzle on demand. No signup, no key, 60 requests an hour per IP. Use it for tutorials, sample apps, or anywhere you need a puzzle without bundling a generator.
Quick start
One GET request. The response is the puzzle and its solution as 81-character strings, plus the seed used to generate them.
curl 'https://api.sudokumountain.com/v1/generate?mode=classic&difficulty=easy'Endpoints
- GET /v1/generate
- Generate a puzzle.
- GET /v1/health
- Health check.
Parameters
- mode
- Required. Only `classic` is supported in v1. Killer is held until the response shape is extended to include cage data.
- difficulty
- Required. One of: easy, medium, hard, expert, master, extreme. The six tiers mirror the play surface.
- seed
- Optional. Non-negative integer up to 4,294,967,295. Same seed yields the same puzzle on the same engine version — useful for tests or stable-per-page demos.
Response
The puzzle and solution are 81-character strings, read left-to-right, top-to-bottom. Each character is a digit 1–9, except in the puzzle where '0' marks an empty cell.
{
"puzzle": "530070000600195000098000060800060003400803001700020006060000280000419005000080079",
"solution": "534678912672195348198342567859761423426853791713924856961537284287419635345286179",
"mode": "classic",
"difficulty": "easy",
"seed": 1734567890
}- puzzle
- 81-character string. '0' marks an empty cell; '1'–'9' marks a given.
- solution
- 81-character string. The unique solution to the puzzle.
- mode
- Echoes the request's mode. Always `classic` in v1.
- difficulty
- Echoes the request's difficulty.
- seed
- The seed actually used. Equals the request's seed when one was provided; otherwise a random 32-bit integer.
JavaScript
const res = await fetch(
'https://api.sudokumountain.com/v1/generate?mode=classic&difficulty=medium',
);
const puzzle = await res.json();
console.log(puzzle.puzzle); // 81-char string, '0' = empty
console.log(puzzle.solution); // 81-char string, all 1–9Python
import urllib.request, json
url = "https://api.sudokumountain.com/v1/generate?mode=classic&difficulty=hard"
with urllib.request.urlopen(url) as r:
puzzle = json.load(r)
print(puzzle["puzzle"]) # 81-char string, '0' = empty
print(puzzle["solution"]) # 81-char string, all 1–9curl — health
curl 'https://api.sudokumountain.com/v1/health'Rate limits
60 requests per hour per IP. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix epoch seconds when the window resets). When you exceed the limit you get a 429 with a Retry-After header.
Errors
Errors return application/json with an `error` code and a human-readable `message`. The codes are stable and safe to switch on:
- 400
- invalid-mode, invalid-difficulty, or invalid-seed. The message tells you what was wrong.
- 429
- rate-limit-exceeded. Retry after the seconds in Retry-After (or X-RateLimit-Reset).
- 500
- generation-failed. Rare. Retry once with a different seed or no seed.
- 503
- rate-limit-unavailable. The counter is offline. Try again in a few seconds.
Versioning
v1 is stable. Breaking changes will ship under v2 with v1 kept indefinitely. New optional parameters and additional response fields are not breaking changes — write parsers that ignore unknown keys.
Use
Free for any use, commercial or otherwise. Attribution is appreciated but not required. If you build something neat, we'd like to hear about it.