Developer docs
Ask fifty verified people a question from your code or your agent, and get a distribution back in about ten minutes. Everything is plain JSON over HTTPS.
Quick start
- Sign in at 50heads.com/app/developers and make an API key. It starts
fh_live_and is shown once. - Estimate the question. It's free and never spends.
- Ask it with an idempotency key, then wait for the result.
curl https://api.50heads.com/v1/billing/estimate \
-H "Content-Type: application/json" \
-d '{"draft":{"type":"single_choice","text":"Which menu would you order from tonight?",
"options":[{"label":"Menu A"},{"label":"Menu B"}],"n":50,"tier":1}}'
curl https://api.50heads.com/v2/questions \
-H "Authorization: Bearer $FIFTYHEADS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"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"}}'
curl "https://api.50heads.com/v2/questions/q_8k2f/wait?seconds=25" \
-H "Authorization: Bearer $FIFTYHEADS_API_KEY"Base URL and versions
https://api.50heads.com. Question endpoints are under /v2; pricing, config, drafts, uploads and billing under /v1. We add fields without notice and never remove or rename one within a version.
Authentication
Send Authorization: Bearer fh_live_…. Keys carry scopes and a daily spend cap, set when you make them. Keys are for servers, CI and scripts: requests from a browser origin are refused. MCP hosts use OAuth instead; see MCP server.
| Scope | Grants |
|---|---|
questions:read | Results, question lists, drafts |
questions:write | Estimate, ask, cancel, share, uploads |
templates:read | Templates (also available without a key) |
account:read | Balance, ledger, what is left under the cap |
Money
Everything is in credits, as integers; the portal and the app show them in your account currency at the day's rate. Asking reserves the full price (per answer × heads); answers not given are refunded when the question closes, is cancelled or is refused.
Idempotency
POST /v2/questions needs an Idempotency-Key header, a UUID you make per question. If a request times out, send it again with the same key: you get the original question back, never a second ask. Keys are kept for 24 hours. The same key with a different body is idempotency_conflict.
Errors
Errors are JSON: { "error": { "code": "…", "message": "…" } }. The message is one plain sentence you can show a person or hand to a model. Branch on code, never on the message.
| Code | HTTP | Meaning |
|---|---|---|
validation | 400 | The body or query failed validation. error.validation lists each field and a fix. |
unauthorized | 401 | No key or token, or it was revoked. MCP responses carry WWW-Authenticate with the resource metadata URL. |
insufficient_scope | 403 | The key or token lacks the scope this call needs. Re-authorise with the scope named in the error. |
not_found | 404 | No such question, draft or result, or it isn't yours. |
idempotency_conflict | 409 | The Idempotency-Key was used with a different body in the last 24 hours. |
insufficient_credits | 402 | Not enough credits for the full price. The error says how many are needed and links to top up. |
spend_cap_exceeded | 402 | The ask would take this key or connection past its daily cap. The error says what is left and when it resets. |
content_refused | 422 | The question breaks the acceptable use policy. Nothing is spent; the error names the category. |
rate_limited | 429 | Too many requests. Wait for Retry-After seconds. |
unavailable | 503 | Asking is paused for maintenance. The error says until when. |
Rate limits
60 requests a minute per key or connection, and 10 asks a minute. Estimates count half. Over the limit you get 429 with Retry-After. Ask us to raise a key's limits.
Pagination
Lists return nextCursor. Pass it back as cursor for the next page; it is null on the last one.
Waiting for results
Don't poll. GET /v2/questions/{id}/wait?seconds=25 holds the request until the question is answered or 25 seconds pass; call it again while done is false. Or register a webhook and we'll tell you.