Generate a Word Search Puzzle Grid
Turn a chosen word list into a complete word search puzzle grid without arranging every letter by hand.
Run — free
Supply the words, a square grid size, and an integer seed; the generator places each word horizontally, vertically, or diagonally, allows compatible letters to cross, and fills every remaining cell with a reproducible letter sequence. The response includes both the finished rows and exact placement coordinates, making it useful for printable activities, automated tests, classroom materials, and applications that need a stable puzzle plus a reliable answer key.
Prepare a word list and choose the grid dimensions
Start with between one and twenty distinct words written with the English letters A through Z. Letter case does not affect the puzzle because the generator normalizes every accepted word to uppercase before placement. Choose <code>grid_size</code> as the number of rows and columns in the square, from 2 through 25. The longest word cannot exceed that size: a six-letter word, for example, needs a grid at least six cells wide because every permitted placement follows a straight line. A larger grid leaves more room between terms and creates more filler cells, while a compact grid tends to produce denser overlaps. If your first size cannot accommodate the full set, increase it or shorten the list rather than expecting words to bend or wrap around an edge. Duplicate words are rejected after case normalization because two identical answer-key entries would be ambiguous. Spaces, hyphens, accents, digits, and punctuation are also rejected, keeping every grid cell to one predictable uppercase character. These validation rules make the returned rows easy to print, split into cells, or compare in software without additional text cleanup.
Use a seed to reproduce placement and filler letters
The <code>seed</code> controls every choice that would otherwise appear random. For each word, the generator builds the complete set of positions that stay inside the grid across east, west, north, south, and the four diagonal directions. It orders those candidates from the seeded pseudo-random sequence, places longer words first, and uses deterministic backtracking when a promising arrangement blocks a later word. Existing letters may be shared only when they match, so crossing words remain readable and no character is overwritten. After all terms have positions, the same seeded sequence supplies uppercase filler letters for blank cells. Repeating the identical words in the identical order with the same grid size and seed therefore returns the same grid and placements. That is particularly useful when a puzzle must be regenerated from a small configuration, reviewed in a test fixture, or shared between a teacher and an answer-key service. Changing the seed usually changes positions and filler, but it does not provide cryptographic unpredictability. Treat the seed as a reproducibility control, not as a secret or a defense against someone intentionally reconstructing the answer.
Read the grid and build an answer key
The response returns <code>grid</code> as an array of equal-length strings, one string per row. This format can be displayed directly in a monospaced layout or converted into individual cells by splitting each string into characters. Coordinates in <code>placements</code> are zero-based: <code>row: 0</code> and <code>column: 0</code> identify the top-left cell. Direction codes describe how to move after each character: <code>E</code> and <code>W</code> are horizontal, <code>N</code> and <code>S</code> are vertical, and <code>NE</code>, <code>NW</code>, <code>SE</code>, and <code>SW</code> are diagonal. Placements remain in the same order as the submitted word list even though the search internally handles longer words first. You can hide that array from players and retain it as the answer key, or use it to highlight solved terms in an interactive interface. If the complete list has no valid arrangement at the requested size, the request returns an input error instead of silently dropping a word or producing a partial puzzle. That all-or-nothing behavior makes automation dependable: a successful result always contains every requested word, while an error tells the caller to select a larger grid or a smaller set.
What you can do with it
Create classroom worksheets
Generate a stable puzzle for students and keep the returned placements as a separate answer key.
Build interactive word games
Render the grid as selectable cells and use coordinates and directions to verify each highlighted word.
Produce reproducible puzzle fixtures
Store a compact word list and seed in tests, then regenerate the exact grid whenever the suite runs.
FAQ
What does one API request cost?
The API price is $0.002 per generated puzzle; the same generator can also run free in the browser.
Will the same seed always produce the same puzzle?
Yes, when the words, their order, the grid size, and the seed are all identical.
Which directions can words use?
Words may run in either direction horizontally, vertically, or diagonally, for eight direction codes in total.
Can two words share letters?
Yes. Words may cross wherever the character required by each word is the same.
What happens when the words do not fit?
The generator returns an invalid input error and does not return a partial grid; increase the grid size or reduce the list.
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/word-search-grid-generate \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"words":["ORBIT","MOON","STAR"],"grid_size":8,"seed":42}'const res = await fetch("https://api.kit.forhosting.com/game/word-search-grid-generate", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"words": [
"ORBIT",
"MOON",
"STAR"
],
"grid_size": 8,
"seed": 42
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/game/word-search-grid-generate",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"words": [
"ORBIT",
"MOON",
"STAR"
],
"grid_size": 8,
"seed": 42
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/game/word-search-grid-generate", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"words":["ORBIT","MOON","STAR"],"grid_size":8,"seed":42}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"words":["ORBIT","MOON","STAR"],"grid_size":8,"seed":42}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/game/word-search-grid-generate", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"words": [
"ORBIT",
"MOON",
"STAR"
],
"grid_size": 8,
"seed": 42
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "game.word_search_grid_generate",
"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.
Limits
max_grid_size | 25 |
max_words | 20 |
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. |