Docs/Reference/Response format

Response format

The three-key envelope every endpoint returns, the five formats it can be serialised as, and why premium fields are absent rather than null.

Every endpoint answers with the same three top-level keys. Only data changes shape, so one response handler covers your whole integration no matter how many endpoints you call.

json
{
  "status": "ok",
  "error": null,
  "data": {
    "joke": "I only know 25 letters of the alphabet. I don't know y."
  }
}
KeyOn successOn failure
status"ok""error"
errornullOne sentence naming what to change
dataThe resultnull
json
{
  "status": "error",
  "error": "The 'email' parameter is required.",
  "data": null
}

Check status rather than inspecting data for emptiness. A successful call that legitimately found nothing still returns status: "ok" with an empty result inside data — that is a valid answer, not a failure.

Response formats

JSON is the default. Four other serialisations are available through the Accept header, and they carry the same envelope rather than a different shape:

FormatAccept headerGood for
JSONapplication/json (default)Everything
XMLapplication/xmlLegacy integrations and enterprise middleware
YAMLapplication/x-yamlConfig pipelines and human-readable fixtures
CSVtext/csvDropping a result straight into a spreadsheet
Markdowntext/markdownPiping a result into a document or an LLM prompt
bash
curl 'https://api.apiverve.com/v1/dadjokes' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Accept: text/markdown'
code
| Field | Value |
| --- | --- |
| status | ok |
| data.joke | I only know 25 letters of the alphabet. I don't know y. |
YAML is application/x-yaml, not application/yaml

Accept: application/yaml is not recognised. It does not error — it falls through to the default and returns JSON with a application/json content type, which is easy to miss if you are not checking. Use application/x-yaml or application/x-yml.

An unrecognised Accept value behaves the same way: JSON, no warning. If you are not getting the format you asked for, check the response's own Content-Type header first — it always reports what you actually received.

Nested data

data is endpoint-specific. Some endpoints return a flat object, some return nested objects, some return an array of results under a named key:

json
{
  "status": "ok",
  "error": null,
  "data": {
    "location": { "city": "London", "country": "GB", "lat": 51.5, "lon": -0.13 },
    "current": { "temperature": 14.2, "humidity": 77, "condition": "Cloudy" }
  }
}

Each endpoint's reference page lists every field it can return, with its type, an example value, and its full dot-path — so location.city in the table is exactly what you index in code.

Premium fields are absent, not empty

This is the one rule worth reading twice, because getting it wrong produces a bug that only appears when a plan changes.

Fields your plan does not include are omitted from the response entirely. They are not null, not 0, not an empty string.

riskScore on email validator is the canonical example — free plans get the verdict fields, paid plans also get the score:

js
// Wrong — treats a premium field you don't have as a real zero
if (data.riskScore === 0) approve();

// Right — presence is the test
if ('riskScore' in data && data.riskScore === 0) approve();
if (data.riskScore == null) { /* not included on this plan */ }

The reasoning: a zeroed field is indistinguishable from a genuine zero, and a fraud score of 0 means "definitely safe" — exactly the wrong default to invent for a customer who is not paying for that field. Absence is unambiguous.

Premium fields are marked on every reference page, so you can see before you call which parts of a response your plan will actually receive — and plans covers which tier includes what.

Field types

Types are stable per field: a field documented as a number is always a number, never a numeric string, and never switches to null to signal absence — an absent value is an absent key.

Two conventions are worth knowing:

  • Dates are ISO 8601 strings (2026-08-18T05:30:00Z) unless the reference page says otherwise. Cycle timestamps in headers, like x-api-renewal, are Unix seconds.
  • Empty collections are [] or {}, not null. A missing collection means the key was not returned at all.

Handling every response the same way

js
async function call(endpoint, params, key) {
  const url = new URL(`https://api.apiverve.com/v1/${endpoint}`);
  Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));

  const res = await fetch(url, { headers: { 'x-api-key': key } });
  const body = await res.json();

  if (body.status !== 'ok') throw new Error(body.error);
  return body.data;
}

That function is complete for every endpoint in the catalog. The only thing that varies is what you do with data.

Next

The headers that select a format are in request headers, and making requests covers parsing and caching what comes back. Any term above that is new is in the glossary.

Status codes and which failures are worth retrying are in error handling. For what a single response costs and how to read your balance from its headers, see rate limits.

Was this page helpful?

Last updated