Response Format
Content type
Section titled “Content type”Every endpoint returns application/json encoded as UTF-8, regardless of the Accept header. Send Content-Type: application/json on requests that carry a body.
Two shapes, deliberately
Section titled “Two shapes, deliberately”Collection endpoints wrap their payload in data; single-object endpoints return their fields at the top level. This is not an accident, and it is worth knowing before you write a parser:
{ "data": [ { "id": 47, "slug": "wavalid-realtime" } ] }{ "balance": "5.230000" }GET /v1/products is the collection case; GET /v1/balance and GET /v1/api-key are the single-object case. The realtime endpoints return the call’s own object — request_id, status, results — at the top level.
Errors
Section titled “Errors”Errors use a fixed two-field object:
{ "code": "invalid_realtime_request", "message": "A realtime request cannot mix phone and email products."}| Field | Meaning |
|---|---|
code | A stable machine-readable identifier. Branch on this. |
message | Human-readable detail. Wording may change; do not parse it. |
The code values are documented per endpoint, together with the HTTP status they arrive with — see Error codes for the full list.
Money appears in two forms, and the difference matters when you parse:
| Where | Type | Example |
|---|---|---|
balance in GET /v1/balance | string | "5.230000" |
total_charged, charged_amount, refunded_amount in realtime responses | number | 0.004000 |
The balance is emitted as a fixed-precision decimal string so that no client ever sees a rounding artefact on a value it is about to compare against a threshold. Realtime amounts are numbers rounded to six decimal places — treat them as approximate at beyond six decimals.
Amounts are in USD, and the currency symbol is never included in the payload. Format the number yourself for display; do not infer the currency from a separator or a prefix.
Timestamps
Section titled “Timestamps”Timestamps are ISO 8601 in UTC, with a Z suffix:
2026-09-13T10:00:00Zchecked_at on a realtime result, and the task timestamps on bulk responses, follow this.
HTTP status is not the outcome
Section titled “HTTP status is not the outcome”Two patterns exist and both are deliberate:
- Realtime: HTTP
200means the call was executed, not that every product answered. Readstatusand each entry’sbilling_status. - Bulk: a submission returns
202-style task acceptance; the work continues asynchronously and the outcome only exists once the task reaches a terminal status.
The rule in both cases: the status code tells you whether to retry, the body tells you what happened.
- Error codes — every
code, per endpoint - Rate limits — the headers that come back with
429