SDKs and examples
The API is small enough to call directly. These examples estimate, ask and wait for one question.
Command line
@50heads/cli is the 50heads command for scripts and CI: ask from flags, a JSON file or a CSV of many questions, wait on the server, and read results and answers. Every command takes --json, and exit codes tell a script what happened. Asks are keyed by a hash of the question, so re-running a script never asks or charges twice.
npx -y @50heads/cli estimate --text "Which name sounds friendlier?" --option Pip --option Moss
# 50 answers, about 10 minutes, £6.00. 12 credits an answer.
export FIFTYHEADS_API_KEY=fh_live_…
npx -y @50heads/cli ask --text "Which name sounds friendlier?" --option Pip --option Moss --wait
npx -y @50heads/cli ask --csv asks.csv --json > asked.jsonl
npx -y @50heads/cli answers q_123 --q price --all --jsonExit codes: 0 done, 2 usage, 3 not signed in, 4 not enough credits or over the cap, 5 the question needs fixing, 6 not found, 7 try again, 8 some bulk asks failed, 124 still live when wait timed out. On your own machine, 50heads login signs in once in the browser, for the CLI and the MCP server alike.
TypeScript
import { randomUUID } from "node:crypto";
const api = "https://api.50heads.com";
const headers = {
Authorization: `Bearer ${process.env.FIFTYHEADS_API_KEY}`,
"Content-Type": "application/json",
};
const draft = {
type: "single_choice",
text: "Which menu would you order from tonight?",
options: [{ label: "Menu A" }, { label: "Menu B" }],
n: 50,
tier: 1,
language: "en",
};
// 1. Price and time, free.
const quote = await fetch(`${api}/v1/billing/estimate`, {
method: "POST",
headers,
body: JSON.stringify({ draft }),
}).then((r) => r.json());
console.log(`${quote.creditsTotal} credits, about ${quote.etaMinutes} min`);
// 2. Ask. The idempotency key makes a retry safe.
const { question } = await fetch(`${api}/v2/questions`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": randomUUID() },
body: JSON.stringify({ draft }),
}).then((r) => r.json());
// 3. Wait on our side, 25 seconds a call, until it is done.
let done = false;
let result;
while (!done) {
({ result, done } = await fetch(`${api}/v2/questions/${question.id}/wait?seconds=25`, {
headers,
}).then((r) => r.json()));
}
console.log(result.summary.note);Python
import os, uuid, requests
API = "https://api.50heads.com"
HEADERS = {"Authorization": f"Bearer {os.environ['FIFTYHEADS_API_KEY']}"}
draft = {
"type": "single_choice",
"text": "Which menu would you order from tonight?",
"options": [{"label": "Menu A"}, {"label": "Menu B"}],
"n": 50,
"tier": 1,
"language": "en",
}
quote = requests.post(f"{API}/v1/billing/estimate", json={"draft": draft}).json()
print(quote["creditsTotal"], "credits, about", quote["etaMinutes"], "min")
question = requests.post(
f"{API}/v2/questions",
json={"draft": draft},
headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
).json()["question"]
done = False
while not done:
body = requests.get(
f"{API}/v2/questions/{question['id']}/wait", params={"seconds": 25}, headers=HEADERS
).json()
result, done = body["result"], body["done"]
print(result["summary"]["note"])MCP, locally
FIFTYHEADS_API_KEY=fh_live_… npx -y @50heads/mcpThe package is on npm as @50heads/mcp and also carries the 50heads Agent Skill: the rules for writing a question heads can answer in five seconds.