Skip to content

HTTP Error Codes

These are the HTTP answers the Werk24 API gives when it refuses a request: a submit to /techread/read-with-callback, and the calls the Python client makes for you. Problems inside a read that was accepted (an unreadable page, an unsupported ask) arrive as exceptions on the messages of that read instead; see Error Handling.

Error Response Format

A refusal from the API carries a JSON body with the status as a string in code and a human-readable message. details and request_id are there when they apply, and left out when they do not:

1
2
3
4
5
6
7
8
9
{
  "code": "415",
  "message": "Unsupported file format. Please provide PDF, PNG, JPEG, or TIFF files.",
  "details": {
    "supported_formats": ["PDF", "PNG", "JPEG", "TIFF"],
    "provided_format": "unknown"
  },
  "request_id": "0a4b3c2d-1e2f-4a5b-8c7d-9e0f1a2b3c4d"
}
  • code: the HTTP status, as a string.
  • message: what went wrong, for a human. Do not parse it.
  • details: more about the refusal. Where there is a machine-readable reason it is in details.error, for example QUOTA_EXHAUSTED or PRIORITY_TOO_HIGH. Branch on that, not on the message.
  • request_id: quote it to support. Present once the request got that far.

A request refused before it reaches Werk24 (by the gateway in front of the API, for example because it is too large) has no body of this shape.

Status Codes

Status When Retry? The Python client raises
400 The request is malformed: a missing or invalid field, a body that is not multipart, an Authorization header that is not two words (Token <key>), or an invalid priority. No: fix the request. BadRequestException; InvalidPriorityError for an invalid priority
401 No token, a scheme other than Token, or a key the API does not know. No. UnauthorizedException
403 priority above your account's tier (PRIORITY_TOO_HIGH). A key refused when the client opens its connection is also a 403. No. PriorityTooHighError; UnauthorizedException for a refused key
too large The request is too large. It is refused before it reaches Werk24, with no body of ours and no request_id; the status depends on the gateway. No: send a smaller request, or use read_drawing. RequestTooLargeException (a 413); versions newer than 2.7.0 raise a subclass of it before sending
415 The drawing is not a supported file format. No. UnsupportedMediaType
429 The account's request allowance is used up (QUOTA_EXHAUSTED). No. It does not reset with time. InsufficientCreditsException from read_drawing_with_callback; see below for read_drawing
5xx A fault on our side. Yes, after a pause. RetryableServerError

400 Bad Request

Examples of what the API sends:

{"code": "400", "message": "Missing required field", "details": {"missing_field": "'callback_url'"}}
{"code": "400", "message": "Invalid request data", "details": {"error": "..."}}

details.error of "Invalid request data" is the validation message; it names the field that failed. Fix the request and send it again; the same request is refused the same way. A refused request is not charged.

401 Unauthorized

{"code": "401", "message": "Invalid or expired authentication token"}

Without an Authorization header the message is "Authorization header missing". Check that the header is Authorization: Token <your-key>, that the key has no stray whitespace, and that it is still listed on the console's Keys page. With the Python client, check which key it found: see Get an API key.

403 Forbidden

1
2
3
4
5
6
{
  "code": "403",
  "message": "Requested priority PRIO1 exceeds account tier PRIO2",
  "details": {"error": "PRIORITY_TOO_HIGH", "account_tier": "PRIO2", "requested_priority": "PRIO1"},
  "request_id": "0a4b3c2d-1e2f-4a5b-8c7d-9e0f1a2b3c4d"
}

Ask for your account's tier or a lower one. See Priority.

413 Request Too Large

A submit to /techread/read-with-callback carries the drawing in its own body, which is limited to about 4.6 MB. A larger request is refused before it reaches Werk24, so the answer has no body of ours and no request_id. See Drawing File Size Limit.

415 Unsupported Media Type

The body is the one under Error Response Format. Convert the drawing to PDF, PNG, JPEG or TIFF; see Supported File Formats.

429: request allowance used up

1
2
3
4
5
{
  "code": "429",
  "message": "...",
  "details": {"error": "QUOTA_EXHAUSTED", "message": "...", "limit": 100}
}

The only 429 the API sends means the account's request allowance is used up. It only happens on accounts that have such an allowance. It does not reset with time, and there is no Retry-After: retrying sends the same refusal. Contact your Werk24 account team to extend the allowance.

A 429 without this body, from the gateway in front of the API, would mean you are sending too fast: back off and retry.

In the Python client, read_drawing_with_callback raises InsufficientCreditsException for any 429, so do not retry it in a loop. read_drawing never sees a 429: a used-up allowance ends the read at its start, surfacing as a validation error or a ServerException. If every read_drawing fails that way, check the allowance before retrying.

5xx Server Errors

1
2
3
4
5
6
{
  "code": "500",
  "message": "An internal error occurred. Please try again later.",
  "details": {"message": "The error has been logged and will be investigated."},
  "request_id": "0a4b3c2d-1e2f-4a5b-8c7d-9e0f1a2b3c4d"
}

We are notified automatically. Retry after a pause. If it persists, contact support with the request_id. On the HTTP route, a 500 is rarely returned for a read that was queued anyway: if callbacks arrive for that request_id, do not submit it again. See the HTTP API page. The Python client does not pass on the request_id of a 5xx, so if a second read would matter, carry your own reference in the callback URL or a callback header and let your receiver drop a duplicate.

Python Client Exception Classes

The Python client turns a refusal into one of these exceptions, all subclasses of TechreadException and all importable from werk24:

Exception Status
BadRequestException 400
InvalidPriorityError 400, invalid priority (has invalid_value)
UnauthorizedException 401 to 403, and a key refused at connect
PriorityTooHighError 403, PRIORITY_TOO_HIGH (has account_tier and requested_priority)
ResourceNotFoundException 404
RequestTooLargeException 413, and (newer than 2.7.0) a callback request checked too large before sending
UnsupportedMediaType 415
InsufficientCreditsException 429 on the HTTP calls, such as read_drawing_with_callback
RetryableServerError 5xx
ServerException any other unexpected status; also the base of InsufficientCreditsException and RetryableServerError

InsufficientCreditsException is a ServerException, so catch it before ServerException, or a spent allowance ends up in your retry path:

import asyncio

from werk24 import (
    AskMetaData,
    BadRequestException,
    InsufficientCreditsException,
    PriorityTooHighError,
    RetryableServerError,
    UnauthorizedException,
    Werk24Client,
)


async def submit(path: str, callback_url: str, attempts: int = 3):
    for attempt in range(1, attempts + 1):
        try:
            async with Werk24Client() as client:
                with open(path, "rb") as drawing:
                    return await client.read_drawing_with_callback(
                        drawing, [AskMetaData()], callback_url
                    )
        except UnauthorizedException:
            raise  # the key: see "Get an API key"
        except PriorityTooHighError as e:
            raise ValueError(f"Your tier is {e.account_tier}") from e
        except BadRequestException:
            raise  # the request itself is wrong; sending it again will not help
        except InsufficientCreditsException:
            raise  # allowance used up: contact your account team, do not retry
        except RetryableServerError:
            if attempt == attempts:
                raise
            await asyncio.sleep(2**attempt)

RequestTooLargeException and the Callback Size Exceptions

A callback request carries the drawing inside its body, and that body is limited to about 4.6 MB, shared with the other form fields (see Drawing File Size Limit). From versions newer than 2.7.0, read_drawing_with_callback checks this limit before sending and raises one of two subclasses of RequestTooLargeException, so an existing except RequestTooLargeException keeps catching both. The example needs such a version; on 2.7.0 its import fails:

import asyncio

from werk24 import (
    AskMetaData,
    CallbackDrawingTooLargeException,
    CallbackFieldsTooLargeException,
    Werk24Client,
)


async def submit(path: str) -> None:
    async with Werk24Client() as client:
        with open(path, "rb") as drawing:
            try:
                request_id = await client.read_drawing_with_callback(
                    drawing, [AskMetaData()], "https://your-server.com/webhook"
                )
                print(f"Registered request: {request_id}")
            except CallbackDrawingTooLargeException as e:
                # Nothing was sent. Use read_drawing (up to 10 MiB) or reduce the file.
                print(f"{e.drawing_bytes} bytes; this request carries at most {e.max_drawing_bytes}")
            except CallbackFieldsTooLargeException as e:
                # callback_headers, public_key, the asks and the file name leave no room.
                print(f"The fields alone take {e.fields_bytes} bytes; this request carries at most {e.max_body_bytes}")


asyncio.run(submit("drawing.pdf"))

A stream that cannot seek (a pipe or a socket) cannot be measured in advance and is sent as is. Version 2.7.0 has neither exception and does not check the size: it sends the whole request, which is refused without a request_id, and the exception it then raises depends on how the gateway answers. So on 2.7.0, check the size yourself before sending, for example with os.path.getsize(path) > 4_600_000, and send larger drawings with read_drawing.

Best Practices

  • Branch on the status and on details.error, never on message. The message is for people and can change.
  • Retry only a 5xx, with a growing pause. Every 4xx sends the same refusal again, and a 429 does not reset with time.
  • Log the request_id of every refusal that carries one, and quote it to support.
  • Check the size before a callback submit: about 4.6 MB for the whole request.

Troubleshooting

"Invalid or expired authentication token" but the key looks correct

  • Check the header is Authorization: Token <your-key>, with the Token prefix and no extra whitespace.
  • Check the key is still listed on the console's Keys page.
  • With the Python client, check which key it found: any license file (.werk24 or werk24_license.txt, in the current folder or your home folder) is read before W24TECHREAD_AUTH_TOKEN. See Get an API key.

429 on every request

  • The account's request allowance is used up. It does not reset with time: contact your Werk24 account team.

A callback request fails and returns no request_id

  • Check the drawing size: a callback request is limited to about 4.6 MB, including the other form fields (Drawing File Size Limit)
  • Submit larger drawings with read_drawing, which takes up to 10 MiB

Repeated 5xx

  • Check System Status
  • Contact support with the request_id if it continues