# Crossword & Word Search Maker (from your word list) (`ars-technis/crossword-word-search-maker`) Actor

Word search, crossword (your clues) or word fill-in from your own word lists. Each grid is checked: every word once, no stray words. JSON, SVG, printable PDF or a 6x9 / 8.5x11 paperback book interior, with answer keys.

- **URL**: https://apify.com/ars-technis/crossword-word-search-maker.md
- **Developed by:** [Ars Technis](https://apify.com/ars-technis) (community)
- **Categories:** Developer tools, Education
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $50.00 / 1,000 puzzle generateds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Crossword & Word Search Maker

Give it a word list and get back finished puzzles: a **word search**, a **numbered crossword that uses your own clues**, or a **word fill-in**, which is a crossword grid with no clues where the solver fits in the listed words. Each puzzle comes as JSON, as SVG images, and in one printable PDF (letter or A4, or a 6x9 / 8.5x11 paperback book interior) with the answer keys at the back.

It's built for teachers and tutors making vocabulary sheets, for newsletter and activity-book makers, for apps that need fresh puzzles, and for AI agents. Language models are poor at drawing letter grids correctly, so an agent can call this tool and get a grid that has been checked.

### What you get

- **Word search.** Every word on your list appears **exactly once** in the grid. Before a puzzle is delivered, the grid is searched in all 8 directions to confirm this. A word that sits inside another listed word (Kansas in Arkansas) gets its own place too; its other copy is part of the longer word, and the item's `alsoInside` field says so.
  - **Offensive-word screen.** The whole finished grid is searched, in all 8 directions, for about 260 slurs, obscenities and sexual or scatological words and their common variants (accents are ignored, so Ü counts as U). The filler letters are redrawn until nothing is spelled, including words formed by filler plus one of your words, or by two of your words in line. Text that lies wholly inside one of your own words (CLASS, SCUNTHORPE) is left alone, because those are your words. The list is our own curated list (`src/data/blocklist.txt`), not an outside vetted one; tests check 300 grids for zero hits and that every entry is caught.
  - Difficulty:
    - easy: words run right and down only
    - medium: adds two diagonals
    - hard: all 8 directions, including backwards
- **Crossword.** Your words are laid out as a connected crossword and numbered Across and Down in the standard way. Each word carries its clue. Every run of two or more letters in the grid is one of your words, so no stray words appear. It is a free-form crossword, not a dense newspaper grid: words cross only where they share a letter, so many cells are blocks. In our tests a quarter to under half of the cells hold letters (a German list of long words gave 25% on an 18x14 grid; lists of 12 short words, 31-44%). If a word shares no letters with the others, you get a clear message saying which word it is, and you are not charged.
- **Word fill-in.** This is the same kind of checked, connected grid as the crossword, with no clues needed. The word list is printed below the grid, grouped by length. This is the classic "fill-ins" page from puzzle books. Every fill-in has **exactly one solution**: a solver tries all ways the words could fit the blank grid, and a layout with two or more fillings is never delivered. If no single-solution grid is found for a list, you get a message and are not charged.
- **Batch.** Give many lists in one run (`wordLists`), and ask for several different versions of each (`versions`), for example one per student so nobody copies. A list that is refused (say, an emoji in a word) or cannot be built is skipped and named in `OUTPUT.problems`, the other lists are still made, and only delivered puzzles are charged.
- **Readable print.** No printed text is smaller than 6 pt, clue numbers and answer keys included. Large grids get a page of their own, or the word list moves on to the next page, instead of smaller type.
- **Printable PDF.** Each puzzle goes on its own page, with the word list or the clues beneath it. The answer keys come at the end.
- **Book interior.** Set `pageSize` to `book-6x9` or `book-8.5x11` to get a paperback interior for your own puzzle book:
  - a title page (`bookTitle`), then one puzzle per page starting on a right-hand page, then the answer keys
  - page numbers, embedded fonts (IBM Plex Sans), an even page count, and grey answer highlights that print well in black and white
  - no bleed, with every mark inside the margins: the inside (gutter) margin follows the page-count table in KDP's paperback guidelines (0.375 in up to 150 pages, 0.5 in up to 300, and so on), and the top and outside margins are 0.5 in or more. The page numbers sit in the bottom margin, at least 0.35 in from the bottom edge (KDP asks for 0.25 in); the puzzles and text above them stay 0.5 in or more from it
  - a long title wraps onto several lines, and a word too long for one line is broken, so no title runs off the page
  - `OUTPUT.pdfPages` gives the page count for your cover's spine. KDP paperbacks need at least 24 pages (give 15 or more word lists with answer keys on, 21 or more without them, or ask for several `versions`) and allow at most about 828 (black and white, white paper), so split very large batches into several books. The cover is yours to make.
- **Reproducible.** The same `seed` with the same input gives the same puzzles again.
- **Plain code.** It uses no AI model and calls no outside service. Your words stay in your own Apify storage.

### Input

| Field | Meaning |
|---|---|
| `puzzleType` | `word-search` (default), `crossword` or `fill-in` |
| `words` | One word or phrase per line. For a crossword, write `word: clue` (clues up to 150 characters; in a word search anything after a colon is ignored). Spaces, hyphens, apostrophes and periods are dropped in the grid (U.S.A. becomes USA). Latin letters only, accented and extended ones included (é, ü, ł, ř, ş, ğ, œ …). The grid is in capitals, and ß becomes SS there, as in German crosswords (Straße is STRASSE in the grid and stays Straße in the word list and clues). |
| `title` | Printed above the puzzle |
| `wordLists` | Optional batch: `[{"title": "Fruit", "words": ["apple", "pear"]}, …]` |
| `versions` | Different puzzles per list, 1–50 (default 1) |
| `difficulty` | Word search: `easy`, `medium` (default) or `hard` |
| `gridSize` | Word search: the grid width and height, 5–30. Crossword and fill-in: the largest allowed width or height (at most 22 for `book-6x9`, so clue numbers stay at 6 pt or more). Leave it empty to size the grid automatically. |
| `includeSolution`, `svg`, `pdf`, `pageSize` (`letter`, `a4`, `book-6x9`, `book-8.5x11`), `bookTitle`, `seed` | Output options |

Limits: 2–60 words per word search and 2–40 per crossword or fill-in, words of up to 30 letters (22 for crosswords and fill-ins on `book-6x9`), and up to 500 puzzles per run. Titles and clues may use Latin (with accents), Greek or Cyrillic letters, which the embedded PDF font (IBM Plex Sans) can print; not Chinese, Japanese or emoji.

### Output

Each puzzle is one dataset item. The PDF is saved to the run's key-value store as `puzzles.pdf`. The `OUTPUT` record holds its link (`pdfUrl`), the page count for book interiors (`pdfPages`), and names any list that could not be built.

Word search, input `{"words": ["cat", "dog", "fish", "bird", "horse", "rabbit"], "title": "Pets", "difficulty": "easy", "seed": 1}`:

```
IHHRGJGH
LOCICYOC
RRRCATSU
ASCHMJBK
BEFKZSFX
BIRDHMIC
IRTOSIST
TCHGHAHZ
```

When `includeSolution` is on (the default), the item's `solution` also lists every word's position, for example `{"word": "horse", "row": 0, "col": 1, "direction": "down"}`, plus an SVG of the grid and one of the answer key.

Crossword, input `{"puzzleType": "crossword", "title": "Instruments", "seed": 2, "words": ["piano: Instrument with 88 keys", "violin: Played with a bow, tucked under the chin", "trumpet: Brass instrument with three valves", "flute: Woodwind held sideways", "harp: Large instrument with strings you pluck", "guitar: Six strings and a sound hole"]}`, answer grid (`#` = block):

```
#G###HARP
#U######I
#I####F#A
#T#VIOLIN
#A####U#O
TRUMPET##
######E##
```

Across: 2 Large instrument with strings you pluck (4) · 5 Played with a bow, tucked under the chin (6) · 6 Brass instrument with three valves (7)
Down: 1 Six strings and a sound hole (6) · 3 Instrument with 88 keys (5) · 4 Woodwind held sideways (5)

The item holds a `blankGrid` to print, the `across` and `down` clues with their numbers, lengths and positions, and the answers. A fill-in item holds the same `blankGrid` and `solutionGrid`, plus `wordsByLength`, for example `{"4": ["DATE", "LIME"], "5": ["APPLE", "GRAPE"]}`.

### Pricing

You pay per puzzle delivered, once: the Actor charges one `puzzle` event per puzzle, and Apify's default per-dataset-item event is removed from the pricing, so you are not billed a second time for the same item. (If that default event were ever priced, the Actor would bill each puzzle through it alone and skip its own event.) A list that is refused or cannot be built, such as a word with no letter in common with the rest, is reported in `OUTPUT` and never charged; the rest of the batch goes ahead. If your maximum cost per run covers fewer puzzles than you asked for, the Actor makes only as many as it covers, and the PDF holds all of them.

### Rights

You may use, print, sell and publish the puzzles you generate, including in books, worksheets and apps. The grids are generated for you. The words and clues are yours.

# Actor input Schema

## `puzzleType` (type: `string`):

word-search = find the listed words in a letter grid. crossword = a numbered crossword built from your words, with your clues. fill-in = a crossword grid with no clues: the solver fits the listed words (grouped by length) into it.

## `words` (type: `array`):

One word or phrase per line (at least 2; up to 60 for word search, 40 for crossword and fill-in; each word 2-30 letters, 3+ for word search; no word twice). Spaces, hyphens, apostrophes and periods are dropped in the grid. Letters: Latin only, accented and extended ones included (é, ü, ł, ř, ş, ğ, œ; ß is written SS in the grid, as in German crosswords); digits, other scripts and emoji are refused. A word inside another listed word (Kansas in Arkansas) is allowed: it gets its own place and also shows inside the longer word. For a crossword, write each line as 'word: clue' (clue up to 150 characters; Latin, Greek or Cyrillic text).

## `title` (type: `string`):

Printed above the puzzle, e.g. 'The Solar System'. Latin, Greek or Cyrillic text, up to 80 characters.

## `wordLists` (type: `array`):

Optional. Several lists in one run, one puzzle each: \[{"title": "Fruit", "words": \["apple", "pear"]}, ...]. Used in addition to 'Words'; the same word rules apply. A list that breaks a rule or cannot be built is skipped, named in OUTPUT.problems and not charged; the other lists are still made.

## `versions` (type: `integer`):

How many different puzzles to make from each list (for example one per student). You are charged per delivered puzzle.

## `difficulty` (type: `string`):

easy = words run right or down only. medium = also diagonally down-right and up-right. hard = all 8 directions, including backwards.

## `gridSize` (type: `integer`):

Word search: the width and height of the grid (5 to 30). Crossword and fill-in: the largest allowed width or height (at most 22 with the 6x9 book page size, so clue numbers stay at 6 pt or more). Leave empty to size automatically.

## `includeSolution` (type: `boolean`):

Add word positions (word search) or the filled grid and answers (crossword), and answer pages in the PDF.

## `svg` (type: `boolean`):

Add a ready-to-embed SVG of each puzzle (and of its answer key).

## `pdf` (type: `boolean`):

Build one PDF with every puzzle on its own page (grid plus word list or clues) and the answer keys at the back. Saved to the run's key-value store as puzzles.pdf.

## `pageSize` (type: `string`):

Paper size of the PDF, or a paperback book interior with no bleed at 6x9 in or 8.5x11 in: a title page, one puzzle per page (long clue lists flow on), answer keys at the back, page numbers, embedded fonts, an even page count, and an inside margin set from the page count by KDP's paperback table. KDP paperbacks need at least 24 pages. You add your own cover.

## `bookTitle` (type: `string`):

Book interiors only: the title on the title page. Default: 'Word Search Puzzles', 'Crossword Puzzles' or 'Word Fill-In Puzzles'.

## `seed` (type: `integer`):

Optional. The same seed and input give the same puzzles again. Leave empty for fresh layouts every run.

## Actor input object example

```json
{
  "puzzleType": "word-search",
  "words": [
    "Jupiter",
    "Saturn",
    "Mercury",
    "Venus",
    "Mars",
    "Neptune",
    "Uranus",
    "Earth",
    "Comet",
    "Asteroid",
    "Galaxy",
    "Orbit"
  ],
  "title": "The Solar System",
  "versions": 1,
  "difficulty": "medium",
  "includeSolution": true,
  "svg": true,
  "pdf": true,
  "pageSize": "letter"
}
```

# Actor output Schema

## `puzzles` (type: `string`):

No description

## `pdf` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "puzzleType": "word-search",
    "words": [
        "Jupiter",
        "Saturn",
        "Mercury",
        "Venus",
        "Mars",
        "Neptune",
        "Uranus",
        "Earth",
        "Comet",
        "Asteroid",
        "Galaxy",
        "Orbit"
    ],
    "title": "The Solar System"
};

// Run the Actor and wait for it to finish
const run = await client.actor("ars-technis/crossword-word-search-maker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "puzzleType": "word-search",
    "words": [
        "Jupiter",
        "Saturn",
        "Mercury",
        "Venus",
        "Mars",
        "Neptune",
        "Uranus",
        "Earth",
        "Comet",
        "Asteroid",
        "Galaxy",
        "Orbit",
    ],
    "title": "The Solar System",
}

# Run the Actor and wait for it to finish
run = client.actor("ars-technis/crossword-word-search-maker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "puzzleType": "word-search",
  "words": [
    "Jupiter",
    "Saturn",
    "Mercury",
    "Venus",
    "Mars",
    "Neptune",
    "Uranus",
    "Earth",
    "Comet",
    "Asteroid",
    "Galaxy",
    "Orbit"
  ],
  "title": "The Solar System"
}' |
apify call ars-technis/crossword-word-search-maker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ars-technis/crossword-word-search-maker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Ye3RyqI80Qn0Jxfxk/builds/Qg7MXyZplMQRZxCWO/openapi.json
