Grandpré cipher
With the keyword rows CODE, ABLE, FISH and TRAP, the word CAT encrypts to 112141: each letter becomes its row and column number on the grid you build. Decrypting reads the pairs back into letters. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Text
- CAT
- Words
- CODE, ABLE, FISH, TRAP
- Calculation mode
- Encrypt
- Result
- 112141
- Length
- 6
- Letters mapped
- 3
- Pairs
- 3
The Grandpré cipher is a classical homophonic substitution that numbers every letter by its coordinates in a grid of keyword rows. You supply the words that fill each row of the checkerboard, paste plain text or a digit-pair stream, and choose encrypt or decrypt. Encryption walks the Latin letters of the message and replaces each one with the two-digit row-column label of the first matching cell in row-major order; decryption collects digits, reads them as pairs, and looks each pair back up on the same grid. Because the same letter can appear more than once among the keyword rows, the system is homophonic in principle—repeated plaintext letters need not always share one code—while this implementation stays fully deterministic by always taking the first cell. When the keyword list does not cover the full alphabet, the response still succeeds for letters that are present and reports incomplete grid status with the list of missing letters so you can enlarge the square.
How Grandpré keyword rows become coordinate pairs
A Grandpré square is built from words laid out one per row so that every cell holds a single Latin letter. Classical descriptions prefer a ten-by-ten board whose ten words also make the first column spell an eleventh word, but any rectangular set of keyword rows with equal letter counts works the same way: strip non-letters, uppercase the rest, and demand that every row ends with the same width between two and ten columns, with at most ten rows.
Axes are labeled from one upward; when a side has ten cells, the tenth label is the digit zero, matching the textbook 1–9–0 convention. Encryption replaces each plaintext letter with the two-digit code formed by its row label followed by its column label. If the letter appears several times in the grid, this tool always selects the first match scanning left to right and top to bottom, so identical inputs always produce identical digit streams without randomness.
Decryption reverses the table lookup: digits are harvested from the input in order, then consumed two at a time as a row label and a column label. An odd digit count, a label outside the active axis set, or a pair that does not address any cell fails immediately. Non-letters in the plaintext are skipped on encrypt so the ciphertext stays pure coordinate digits; non-digit characters on decrypt are ignored while the digit stream is assembled.
There is no modular alphabet shift and no secondary additive key—only the open keyword grid—so the cipher stands or falls on how completely and carefully those rows are chosen.
Where Grandpré tools show up in real work
Introductory cryptography and puzzle-club sessions still assign Grandpré-style grids so students practice homophonic coordinate substitution before they meet machine ciphers; a single trusted oracle that encrypts and decrypts from explicit keyword rows speeds demos and auto-graders. Capture-the-flag warm-ups and geocaching challenges hide digit-pair messages beside a published word square, and solvers want a fast, predictable first-match encoder rather than a general cryptanalysis suite.
Content and support teams sometimes lightly encode spoilers, quiz answers, or internal labels as coordinate pairs over a shared word list and need the reverse path without installing a language-specific crypto library. If you need a fixed five-by-five Polybius square without keywords, a Playfair digraph square, a Nihilist additive layer, Caesar or Vigenère letter shifts, or automatic recovery of an unknown Grandpré board from ciphertext alone, treat those as separate search intents and landings rather than overloading this keyword-row coordinate cipher page.
What you can do with it
Classroom homophonic coordinate demos
Encrypt and decrypt sample words on a published keyword-row grid so intro-crypto lessons share one ground-truth mapping and see incomplete-grid coverage reports.
Puzzle and CTF digit-pair challenges
Encode or reverse Grandpré coordinate streams against a known word square without writing a one-off script for every problem set.
FAQ
What is the Grandpré cipher?
A classical homophonic cipher that places letters in a grid of keyword rows and replaces each plaintext letter with the two-digit row-column coordinates of a matching cell.
How do I supply the grid?
Pass words as an array of strings, one keyword row per entry. All rows must have the same letter count after non-letters are stripped. Sizes from 2×2 up to 10×10 are accepted.
What does incomplete grid mean?
Encrypt still works for letters that are present; it fails only when the plaintext needs a missing letter.
Which coordinate is chosen when a letter appears more than once?
The first cell in row-major order (left to right, top to bottom). That keeps encryption deterministic and free of randomness while still allowing homophones on the board.
What does it cost?
The browser tool is free and runs locally.
Related tools
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/enc/grandpre \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"CAT","words":["CODE","ABLE","FISH","TRAP"]}'const res = await fetch("https://api.kit.forhosting.com/enc/grandpre", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "CAT",
"words": [
"CODE",
"ABLE",
"FISH",
"TRAP"
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/enc/grandpre",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "CAT",
"words": [
"CODE",
"ABLE",
"FISH",
"TRAP"
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/enc/grandpre", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"CAT","words":["CODE","ABLE","FISH","TRAP"]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"CAT","words":["CODE","ABLE","FISH","TRAP"]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/enc/grandpre", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"text": "CAT",
"words": [
"CODE",
"ABLE",
"FISH",
"TRAP"
]
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "enc.grandpre",
"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. |
Technical notes
How Grandpré keyword rows become coordinate pairs
Letters that never appear among the supplied words cannot be encrypted: the request fails with invalid_input and names the incomplete-grid gap rather than inventing a substitute.
What you send and what you get back
Send a required string field named text, a required array field named words with one keyword string per grid row, and an optional mode field set to encrypt or decrypt. Classic checks include encrypting CAT on the sample four-by-four words CODE, ABLE, FISH, TRAP into a six-digit stream, decrypting that stream back to CAT, confirming incomplete is true when letters such as G or Z are absent, and rejecting a five-digit ciphertext rather than silently truncating. When mode is omitted, encrypt is the default so a minimal payload still produces coordinate pairs. On success the response echoes mode and text, returns result as the digit stream or the recovered uppercase letters, length of that result, letters_mapped and pairs as how many cells were used, digit_count for the coordinate stream, rows and cols for the grid size, grid as the normalized uppercase keyword rows, incomplete as a boolean, missing_letters as the A–Z characters absent from the board, alphabet_coverage as how many distinct letters the words supply, row_labels and col_labels as compact axis strings, and cipher set to grandpre for clients that assert the profile they hit. Missing text, missing words, fewer than two rows, more than ten rows, a row without Latin letters, uneven row lengths after stripping non-letters, unsupported mode values, strings longer than the documented maximum, encrypt payloads with no letters, decrypt payloads with no digits, an odd number of digits, and coordinate pairs outside the labeled axes all fail with invalid_input and a clear English message. Validation failures do not consume prepaid balance. Pricing for successful API calls is the published base of $0.002 per item.
Where Grandpré tools show up in real work
Because this capability is a pure parse engine, API pricing is the published base of $0.002 per successful item and failed validation does not consume balance.
Integration use cases
Shared word-list label encoding
Turn quiz answers or internal labels into row-column digit pairs over an agreed keyword list and decode them later with the same contract in browser or API.