Reference
API reference
The complete contract for POST /v1/generate, plus the supporting endpoints, error codes and limits.
Reading this with an AI agent?
The entire documentation lives in one Markdown file, built to be fetched and read by an LLM. Copy the prompt below and paste it into ChatGPT, Claude, Cursor or any agent with web access — it asks the agent to read the file before helping you.
POST /v1/generate
Base URL https://api.searchops.io. This is the only endpoint needed to generate documents.
Headers
| Header | Required | Value |
|---|---|---|
x-api-key | yes | Your sk_live_… key. May also be sent as api_key in the body, but the header is preferred. |
Content-Type | yes | application/json |
Body
| Field | Type | Default | Notes |
|---|---|---|---|
content | string | — | Required. Max 500 KB in UTF-8. |
input_type | markdown | html | json | markdown | Alias: input |
response_format | base64 | url | file | base64 | Alias: format |
filename | string | document.docx | 1–120 chars, no /, \ or control characters. .docx appended if missing. Unicode supported. |
options | object | — | Validated but currently ignored — see below. |
Unknown fields are ignored, not rejected. A typo like file_name instead of filename does not fail the request — the field is dropped and the default is used, so you get document.docx with no error. When a value seems to have no effect, check the spelling against this table.
Responses
file returns raw bytes with these headers:
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document Content-Disposition: attachment; filename="report.docx"; filename*=UTF-8''report.docx X-Generation-Time-Ms: 480
base64 and url return JSON:
{
"status": "success",
"format": "url",
"data": "https://api.searchops.io/v1/download/550e8400-e29b-41d4-a716-446655440000",
"pages": null,
"generation_time_ms": 480
}pages is always null. Download links from url live for 24 hours.
Formatting options are not active
The API accepts and validates an options object — font, font_size, header_text, footer_text, page_size and the four margin_* fields — but it does not change the document yet. Styling comes from a fixed template (Roboto, A4).
When you send it, the API says so: JSON responses gain a warnings array, and file responses gain an X-DocxAPI-Warning header. Do not depend on these fields until this note is removed.
Error codes
{ "error": { "code": "INVALID_API_KEY", "message": "Invalid or inactive API key." } }| HTTP | code | Meaning | Retry? |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Body failed schema validation — usually an unknown field or wrong enum | No |
| 401 | INVALID_API_KEY | Missing, wrong, inactive or deleted key | No |
| 402 | CREDITS_EXHAUSTED | Monthly quota used up | No |
| 403 | DOMAIN_NOT_ALLOWED | Request origin not on the key's allow-list | No |
| 413 | CONTENT_TOO_LARGE | content over 500 KB | No |
| 413 | OUTPUT_TOO_LARGE | Generated file exceeded the output limit | No |
| 429 | RATE_LIMIT_EXCEEDED | Over 60 requests/minute for this key | Yes |
| 500 | PANDOC_ERROR | Conversion failed — usually malformed JSON content | No |
| 422 | CONVERSION_ERROR | Content could not be converted to block markup (Gutenberg). Not charged. | No |
| 503 | GENERATION_BUSY | Conversion queue is full | Yes |
| 503 | USAGE_SERVICE_ERROR | Quota service temporarily unavailable | Yes |
| 504 | GENERATION_TIMEOUT | Conversion exceeded 30 seconds | Reduce size first |
Every response carries X-Request-Id. Include it when reporting a problem.
Plans and limits
| Plan | Documents / month | Overage | API keys |
|---|---|---|---|
| Free | 20 | — | 1 |
| Starter | 200 | $0.05 | 1 |
| Pro | 1,000 | $0.03 | 3 |
| Agency | 3,000 | $0.02 | Unlimited |
- Only successful generations count against quota. Failed requests are logged, not charged.
- The month is the calendar month in UTC.
- Free plan documents carry a watermark line at the end.
- Paid plans keep working past quota and are billed for overage.
- Extra credits come in blocks of 50 (min 50, max 10,000) and are used after the plan quota.
| Technical limit | Value |
|---|---|
Max content size | 500 KB |
| Conversion timeout | 30 seconds |
| Rate limit | 60 requests / minute per API key |
| Download link lifetime | 24 hours |
| Max request body | 10 MB |
POST /v1/gutenberg/convert
Converts the same three input formats into WordPress block markup. Consumes one credit from the same balance as the DOCX endpoint.
Body
| Field | Type | Default | Notes |
|---|---|---|---|
content | string | — | Required. Max 500 KB. |
input_format | markdown | html | json | markdown | Aliases: input_type, input |
profile | portable-article | portable-article | Core blocks only, no theme or plugin dependencies |
options.h1_policy | demote | keep | remove | demote | The post title already renders as the page H1 |
options.unknown_node_policy | html | drop | strict | html | What to do with content that has no block mapping |
options.return_ast | boolean | false | Include the canonical tree in the response |
Response
| Field | Meaning |
|---|---|
gutenberg | The block markup. Goes straight into the WordPress content field. |
blocks | Block types produced, e.g. core/heading. |
validation | syntax and round_trip. Markup that fails either is never returned. |
warnings | Content that came through in a different shape. |
losses | Content that did not come through at all. |
serializer | Profile, catalog date and API version used. |
Conversion runs before the credit is reserved, so invalid input and failed round trips cost nothing. A 422 CONVERSION_ERROR is never charged.
POST /v1/gutenberg/parse
Takes block markup and returns its structure. Useful for diagnosing a post that is already broken. Requires an API key but does not consume credits. Malformed markup returns 400 INVALID_BLOCK_MARKUP with the specific reason.
Supporting endpoints
GET /v1/download/:id
Serves a file produced with response_format: "url". No authentication — the UUID is the capability, so treat the link as a secret. Returns 404 once the 24 hours elapse.
GET /health
No authentication. Useful for monitoring.
{
"status": "ok",
"pandoc_available": true,
"version": "3.1.3",
"generation": { "active": 0, "queued": 0, "max_concurrency": 2, "max_queue": 50 }
}Dashboard endpoints
The dashboard uses a session JWT rather than an API key: /v1/auth/me, /v1/auth/profile, /v1/keys (including /v1/keys/:id/rotate), /v1/usage and /v1/billing/*. You do not need any of them to generate documents.
Quick reference
POST https://api.searchops.io/v1/generate
header x-api-key: sk_live_…
body { content, input_type?, response_format?, filename?, options? }
input_type markdown | html | json default markdown (alias: input)
response_format base64 | url | file default base64 (alias: format)
filename 1–120 chars, .docx added default document.docx
content required, ≤ 500 KB
GET /v1/download/:id 24h link from response_format=url
GET /health status + conversion queue