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–9

Python

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–9

curl — 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.

Related