AI Generation
Generate a poll or quiz draft from an article. Generation runs async — a job is queued, and the resulting draft is reviewed, edited, and published separately. See Drafts.
Send an article (by URL or raw text) and get back a draft, not a live poll/quiz — nothing is published until you (or auto_publish) say so. See Drafts for reviewing, editing, and publishing the result.
Every question is grounded in the article's own text — the model is instructed never to invent facts, and self-reports is_grounded per question and an overall confidence. A draft with an ungrounded question, or whose confidence falls below your account's minimum, is flagged: it still appears for review, but can never auto-publish. By default, submitting an article that's substantially about a tragedy, an active election, or a health crisis skips generation entirely and the job comes back flagged with no draft at all — pass check_sensitive_topics: false to turn this screening off.
Plan limits
Each plan includes a monthly allowance of AI generations, shared across every site on the account (see your plan's details). A repeat save of the same article (same text, type, and question_count) always reuses the first run's draft and never counts against this allowance. Once it's used up, POST /generate returns 429 Too Many Requests until the monthly reset.
Trait-mapping suggestions and auto_publish require the Pro plan or above. On Free or Starter, a draft is generated normally but with no suggested trait mappings, and auto_publish is silently ignored (the draft is always created for review) — both are called out in the job's notices, alongside an upgrade_url.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /generate | API key | Queue a poll/quiz generation job |
| GET | /generate/{job} | API key | Check a generation job's status |
/generate
API key
Queue a generation job
| Field | Type | Rules |
|---|---|---|
| article_url | string | Required unless article_text is given (exactly one of the two) — the page is fetched and its readable text extracted server-side. |
| article_text | string | Required unless article_url is given — skip the fetch and hand over the article's text directly. |
| type | string | Required, one of poll, quiz. (Not survey — a poll is a single question; a true multi-step survey isn't generated by this endpoint.) |
| question_count | integer | Required, 1 up to your account's configured maximum. A poll is always exactly 1 question. |
| tone | string | Optional — a short style hint passed to the model, e.g. "playful" or "serious". |
| auto_publish | boolean | Optional, defaults to false. Ignored (never publishes) if the resulting draft is flagged, regardless of this value. |
| check_sensitive_topics | boolean | Optional, defaults to true. Set to false to skip the sensitive-topic screening entirely — the job is never flagged for subject matter (an ungrounded question or low confidence can still flag it). |
| webhook_url | string | Optional — POSTed once the job finishes (completed, failed, or flagged), unsigned. See the payload shape below. |
| site_id | integer | Optional, same rules as Poll's site_id. |
curl -X POST https://qnaapi.com/api/v1/generate \
-H "X-API-Key: qna_..." \
-H "Content-Type: application/json" \
-d '{
"article_url": "https://example.com/news/playoff-recap",
"type": "quiz",
"question_count": 5,
"auto_publish": false
}'{
"data": {
"job_id": 42,
"status": "pending",
"type": "quiz",
"draft_id": null,
"error_message": null,
"flagged_reason": null,
"model": null,
"prompt_version": null,
"input_tokens": null,
"output_tokens": null,
"latency_ms": null,
"notices": [],
"upgrade_url": null,
"started_at": null,
"completed_at": null,
"created_at": "2026-10-02T00:00:00.000000Z"
}
}type and question_count) reuses the first run's draft and skips generation entirely — no credit is used on a repeat save of the same post.
Once your monthly AI generation credits are used up:
{
"message": "You've used all 25 AI generation credits for this plan this month.",
"error": "generation_credits_exhausted",
"credits_reset_at": "2026-11-01T00:00:00+00:00",
"upgrade_url": "https://qnaapi.com/checkout/pro"
}On Free or Starter, requesting auto_publish or receiving trait suggestions that your plan doesn't support shows up in the created job instead of an error:
{
"data": {
"job_id": 43,
"status": "pending",
"notices": [
"auto_publish requires the Pro plan or above — this draft was generated for review only.",
"Trait mapping suggestions require the Pro plan or above — this draft has no suggested trait mappings."
],
"upgrade_url": "https://qnaapi.com/checkout/pro"
}
}If webhook_url is given, this JSON body is POSTed to it once the job reaches a final status:
{
"job_id": 42,
"status": "completed",
"draft_id": 7,
"error": null,
"flagged_reason": null
}/generate/{job}
API key
Check a generation job
curl -H "X-API-Key: qna_..." https://qnaapi.com/api/v1/generate/42
| Status | Meaning |
|---|---|
pending | Queued, not yet started |
processing | Fetching the article and/or generating |
completed | A draft was created — see draft_id |
flagged | Skipped generation — the article matched a sensitive-topic rule. No draft was created; see flagged_reason. |
failed | Couldn't fetch the article, or generation failed twice in a row — see error_message. |
credits_exhausted | Rare — credits ran out between the initial request and the job actually running. No draft was created; see upgrade_url. |
A completed job's draft may itself be flagged for low confidence or an ungrounded question — that's a property of the draft, not the job. Check the draft's own flagged_reason once you fetch it.