invalid_request
HTTP 400 · not billed
{ "error": {
"code": "invalid_request",
"message": "…",
"docs_url": "https://pdfcraft.dev/errors#invalid_request"
} }When you get it
Both html and url were supplied, or neither, or an option is out of range.
The body did not survive validation at the boundary. Most often that is both html and url in one request, or neither — the contract requires exactly one, because a request carrying both has no single correct answer.
How to fix it
- Read the message: it names the offending field rather than saying "invalid body".
- Send exactly one of html, url or file.
- Check option ranges — scale is 0.1 to 2.0, timeoutMs caps at 120000.
- For extraction, a schema value must be one of the documented types; "money" is not one, "currency" is.
Is it billed?
No. A request is billed only when Chromium actually ran. Almost always on the first integration attempt, and almost never afterwards.
Reproducing it
curl -i -X POST https://api.pdfcraft.dev/v1/render \
-H "Authorization: Bearer $PDFCRAFT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"html":"<h1>hi</h1>"}'The -i matters: Retry-After and X-Renders-Remaining are headers, and a client that only reads the body throws away the two numbers that tell it what to do next.
Every code
| HTTP | Code | Billed? |
|---|---|---|
| 400 | invalid_request | free |
| 401 | invalid_api_key | free |
| 402 | payment_required | free |
| 404 | not_found | free |
| 408 | render_timeout | free |
| 422 | render_failed | billed |
| 429 | rate_limited | free |
| 429 | quota_exceeded | free |
| 429 | demo_busy | free |
| 415 | unsupported_file | free |
| 422 | extraction_failed | free |
| 500 | internal_error | free |