Guides
Generations & streaming
Lifecycle
POST /articles returns a generation in queued state. A worker picks it up within seconds, runs the pipeline and saves the article. Most articles finish in one to three minutes; pillar articles on premium models can take longer.
| status | Meaning |
|---|---|
queued | Waiting for a worker. Can still be canceled (credits are refunded). |
running | The pipeline is working; see stage and progress. |
succeeded | The article is ready — fetch it via article_id. |
failed | Something went wrong; error.code and error.message explain why. Credits were refunded. |
canceled | Canceled while queued. Credits were refunded. |
While running, stage and progress (0–100) tell you where it is:
| stage | progress | What happens |
|---|---|---|
queued | 0 | Waiting for a worker. |
extracting | 5 | URL sources only: fetching and cleaning the source page. |
outlining | 12 | Planning title, focus keyword and sections. |
writing | 20–80 | Writing the introduction and sections (several in parallel); images are generated alongside. |
optimizing | 80 | SEO pack: meta title and description, slug, excerpt, tags, FAQ. |
finishing | 92 | Assembling, sanitizing and scoring the article. |
done | 100 | Finished. |
Polling
The simplest integration: check every 5–10 seconds until the status is terminal. Polling counts toward your rate limit (120 requests per minute per key), so don't go below a couple of seconds.
curl https://reblogger.ai/api/v1/generations/$GENERATION_ID \
-H "Authorization: Bearer $REBLOGGER_API_KEY"Streaming events (SSE)
For live UIs, open the text/event-stream. You receive the outline as soon as it's planned, then the article text as it's written. The stream replays all events from the beginning (or from Last-Event-ID), sends a comment ping every 15 seconds, and closes after done or error.
retry: 2000
id: 812
event: stage
data: {"stage":"writing","progress":20}
id: 813
event: section_delta
data: {"section":-1,"text":"Growing vegetables on a balcony is easier than "}
id: 840
event: done
data: {"articleId":"cmh1x0art9q3m0001","title":"How to Start a Vegetable Garden on a Balcony","words":1532}EventSource and headers
The browser's EventSource can't send an Authorization header, and API keys don't belong in the browser anyway. Consume the stream on your server with fetch (below) and relay what you need to your frontend.
const res = await fetch('https://reblogger.ai/api/v1/generations/' + generationId + '/events', {
headers: { Authorization: `Bearer ${process.env.REBLOGGER_API_KEY}` },
});
const decoder = new TextDecoderStream();
let buffer = '';
for await (const chunk of res.body.pipeThrough(decoder)) {
buffer += chunk;
let end;
while ((end = buffer.indexOf('\n\n')) >= 0) {
const raw = buffer.slice(0, end);
buffer = buffer.slice(end + 2);
const event = /^event: (.+)$/m.exec(raw)?.[1];
const data = /^data: (.+)$/m.exec(raw)?.[1];
if (!event || !data) continue; // retry hints and pings
const payload = JSON.parse(data);
if (event === 'section_delta') process.stdout.write(payload.text);
if (event === 'done') console.log('\nArticle ready:', payload.articleId);
if (event === 'error') throw new Error(payload.message);
}
}Event reference
Event payloads mirror the live pipeline and use camelCase keys. Treat unknown event types as informational — we may add new ones.
| event | data | Description |
|---|---|---|
stage | { stage, progress } | The pipeline moved to a new stage. |
source | { title, url, words, via } | URL sources: the extracted source page. |
outline | { outline: { title, focusKeyword, sections: [{ heading, keyPoints, targetWords }] } } | The planned structure. |
section_delta | { section, text } | A chunk of streamed Markdown for a section. Section -1 is the introduction. |
section_done | { section, words } | A section is complete. |
image | { url, alt, width, height, section } | An image is ready (section null = cover). |
seo | { seo: { metaTitle, metaDescription, slug, tags, faq, … } } | The SEO pack. |
done | { articleId, title, words } | Terminal: the article is saved. The stream closes. |
error | { code, message } | Terminal: the generation failed or was canceled. The stream closes. |
Resuming
Every event has a numeric id. If the connection drops, reconnect with the header Last-Event-ID: 813 (or the query parameter ?last_event_id=813) to receive only newer events. Proxies that buffer responses can delay events — the stream already sends X-Accel-Buffering: no for nginx.
Canceling
Generations that are still queued can be canceled and are refunded in full. Once a worker has started, the generation runs to completion (409 conflict). Deleting a queued article via DELETE /articles/{id} cancels it too.