← Rhyme Finder / API
Your token

Rhyme Finder API

The word lookups and couplet mode run entirely in the browser on the CMU Pronouncing Dictionary, so they have no API: there is nothing to call. What you can call is the Next line lane: send the lines of a poem or lyric and get five candidate next lines back, each aimed at a rhyme word and a syllable count. It uses a language model (gpt-terra), needs a signed-in SkillSafe token and is billed in credits.

Over the API you get the model's reply as written. The web page then re-checks every line with its own dictionary (rhyme type of the last word, syllable count) and moves failures to the bottom. Your code should do the same check before trusting a line.

Base URL, tokens and the response shape

Base URL: https://api.skillsafe.ai/v1/app-api. There is no app segment in the path: the app is bound to the token when it is minted. Every response is {"ok": true, "data": {...}} or {"ok": false, "error": {"code", "message"}}.

StatusCodeWhat to do
401unauthorizedThe token is missing, stale or revoked. Sign in again on the token page.
402insufficient_creditsYour balance is below the run's minimum. Top up at skillsafe.ai/account/billing/.
403forbiddenA guest token tried to run. Use a personal token.
429rate_limitedWait and retry with the same Idempotency-Key.
5xxserver_errorTransient. Retry with the same Idempotency-Key so a retry never bills twice.

The input

The body of /estimate and /run is the input object itself (not wrapped in {"input": ...}).

FieldTypeMeaning
taskstringAlways "next_line".
linesstring[] (1–4)The lines so far, oldest first. The page keeps the last four and the last 200 characters of each.
schemestring"AABB": the new line rhymes with the last line. "ABAB": with the line before the last.
rhyme_withstringThe word the new line must rhyme with (the last word of the line the scheme picks).
target_syllablesinteger 2–24Syllables the new line should have; the page uses line 1's count unless the user sets one.
perfect_rhymesstring[]Common perfect rhymes for rhyme_with, most common first (the page sends up to 40).
near_rhymesstring[]Common near rhymes (the page sends up to 24).
themestring, optionalWhat the next line should be about (up to 160 characters).
tonestring, optionalOne of tender, playful, melancholy, hopeful, wry, bold, dark.
avoidstring[], optionalLines already shown; the model writes five new ones with new end words.
retry_notestring, optionalOnly on a reformat retry: why the previous reply could not be read.
{"task":"next_line",
 "lines":["We waited by the harbour wall","the gulls were wheeling overhead"],
 "scheme":"ABAB","rhyme_with":"wall","target_syllables":8,
 "perfect_rhymes":["all","call","small","fall","ball","hall","tall","recall"],
 "near_rhymes":["called","fault","falls","salt","doll"],
 "theme":"a boat that is late coming home","tone":"hopeful"}

Leaving out perfect_rhymes and near_rhymes works, but the lines rhyme less reliably: the lists are what keep the model on true rhymes.

The reply

The job's output.output is a string holding one JSON object:

{"task":"next_line","rhyme_with":"wall","target_syllables":8,"suggestions":[
  {"line":"and then a sail rose, white and tall","end_word":"tall","syllables":8,
   "rhyme":"perfect","why":"the late boat finally appears"}
 ],"note":""}

Steps: token, /me, /estimate, /run

Save the input above as input.json. /estimate is free and creates no job: it shows the credits a run reserves (hold_credits), the minimum balance (min_credits) and the model. /run is billed: send an Idempotency-Key built from the input and an attempt number, so a resend after a network blip never bills twice. The page uses rhyme-finder:next_line:<inputHash>:a<attempt>; its one automatic reformat retry is attempt 2 and is shown in the page as one extra run.

BASE=https://api.skillsafe.ai/v1/app-api
TOKEN="YOUR_TOKEN"   # personal token from https://rhyme-finder.skillsafe.ai/tokens.html
api() { curl -s -X "$1" "$BASE$2" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" "${@:3}"; }

api GET /me | jq '.data | {subject_type, credits}'
api POST /estimate --data @input.json | jq '.data | {hold_credits, min_credits, model}'

KEY="rhyme-finder:next_line:$(shasum -a 256 input.json | cut -c1-16):a1"
JOB=$(api POST /run -H "Idempotency-Key: $KEY" --data @input.json | jq -r '.data.job_id')
while :; do
  J=$(api GET "/jobs/$JOB"); S=$(echo "$J" | jq -r '.data.status')
  case "$S" in succeeded|failed|cancelled) break;; esac; sleep 2
done
echo "$J" | jq '.data | {charged_credits, truncated}'
echo "$J" | jq -r '.data.output.output' | jq -r '.suggestions[] | "\(.line)   [\(.end_word), \(.syllables)]"'

To mint a guest token for /me and /estimate only: curl -s -X POST "$BASE/guest" -H "Content-Type: application/json" -d '{"slug":"rhyme-finder"}' | jq -r .data.token.