Docs/Platform/Writing queries

Writing queries

Query syntax, arguments, calling several APIs in one request, the explorer, and handling GraphQL-shaped errors code by code.

Every API in the catalog is a field on the root query type, named by its API id — the same id as its reference page. /ref/dadjokes is the dadjokes field. An id containing a hyphen loses it: the field name is the id lowercased with hyphens removed.

You need an account and a key before anything on this page will run; quickstart covers both. Prior GraphQL experience helps but is not assumed — the query language is small, and everything you need is on this page.

The explorer

The fastest way to write a query is not to write it in code. The GraphQL explorer in the dashboard is a browser-based editor with four panels:

  • Query editor — with autocomplete driven by the live schema, so field names are suggested rather than guessed.
  • Variables — a JSON object bound to the query's declared variables.
  • Results — the response, including errors.
  • Docs — the introspected schema. This is the authoritative list of what each API returns, and it is where to check a field name that GraphQL has normalised.

You have to be signed in; the explorer uses your account's key, so queries run there cost real credits exactly as they would from code.

Basic syntax

A query names fields, and the response mirrors the query's shape:

query {
  dadjokes {
    joke
  }
}

Ask for fewer fields and you get fewer fields. That is the whole point of the format — there is no "full response" to trim client-side.

Arguments

Parameters go inside a single input object, not as loose arguments. This is uniform across every API, so once you have written one call you have written all of them:

graphql
query {
  agecalculator(input: { dob: "1990-01-01" }) {
    age_years
    age_months
    age_days
  }
}

The keys inside input are the same parameter names the endpoint's reference page documents. What that page calls dob, GraphQL calls dob.

Response field names are the exception. GraphQL identifiers cannot contain hyphens, dots, spaces, colons, slashes or plus signs, so any REST field name with one is rewritten:

REST fieldGraphQL field
age.yearsage_years
content-typecontent_type
2024_total_2024_total

The rule is: - + : . / \ and space become _, anything else outside [A-Za-z0-9_] is dropped, and a leading digit gets an underscore in front. When in doubt, read the docs panel in the explorer rather than transliterating by hand.

Variables

Hardcoding values into a query string means rebuilding the string per request. Declare variables instead and send them as a separate JSON object:

query GetAge($dob: String!) {
  agecalculator(input: { dob: $dob }) {
    age_years
  }
}

This is also the safe option: a value interpolated into a query string can break the query's syntax, and a variable cannot.

Querying several APIs at once

Name more than one root field and they all run:

query {
  advice { id advice }
  dadjokes { joke }
  randomquote { quote author }
}

The three APIs are called in parallel, so the query costs about as much wall-clock time as the slowest one rather than the sum. Against three sequential REST calls that is usually the largest win available here.

Cost is per API, not per query: three 1-credit APIs cost 3 credits. And each one counts individually against your rate limit — a query naming five APIs is five calls as far as the per-minute ceiling is concerned, even though it is one HTTP request.

Free plans are limited to one API per query; paid plans allow several. Exceeding the cap is a 403 raised before anything runs, so an over-limit query costs nothing and the error message names your actual ceiling.

Making the request from code

The endpoint is https://api.apiverve.com/v1/graphql, it is POST only, and it authenticates with the same x-api-key header as every REST endpoint.

curl -X POST https://api.apiverve.com/v1/graphql \
  -H "x-api-key: $APIVERVE_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"query":"query { dadjokes { joke } }"}'

No GraphQL client library is required — the request is a POST with a JSON body. If you already use Apollo, urql or graphql-request, they work as-is; point them at the endpoint and set the header.

Set a client timeout of at least 35 seconds. Each API in a query gets 30 seconds at the gateway, so a client that gives up at 10 will abandon queries that were about to succeed.

Handling errors

GraphQL does not use the REST envelope. There is no status field to check. Instead, a response may carry data, errors, or both:

json
{
  "errors": [
    {
      "message": "Insufficient credits. Need 10, have 5",
      "extensions": { "code": "INSUFFICIENT_CREDITS" }
    }
  ]
}

Branch on extensions.code, not on message — the code is stable, the sentence is written for a human and may be reworded.

codeHTTPMeaning
METHOD_NOT_ALLOWED405You sent a GET. Use POST.
GRAPHQL_NOT_SUPPORTED400This host does not expose GraphQL.
UNAUTHORIZED401No x-api-key header.
INVALID_API_KEY401The key is not recognised.
BAD_REQUEST400No query in the body, or an endpoint rejected the input.
GRAPHQL_VALIDATION_ERROR400The query does not match the schema — usually a misspelled field.
FILE_UPLOAD_NOT_SUPPORTED400An API in the query takes a file. Call it over REST.
PLAN_LIMIT_EXCEEDED403More APIs in one query than the plan allows.
INSUFFICIENT_CREDITS402Balance will not cover the query. Nothing was called.
FORBIDDENAn API refused this key — scoping, IP allow-list, or revocation.
RATE_LIMITEDAn API returned 429. Back off and retry.
SERVICE_UNAVAILABLEAn upstream source is down. Retry with backoff.
INTERNAL_SERVER_ERROROur side failed. Retry with backoff.
NETWORK_ERRORThe gateway could not reach the API. Retry with backoff.

The rows with no HTTP status are per-API failures raised inside a query that otherwise ran, which is where the format gets genuinely different from REST.

Partial success is the case to get right

If a query names three APIs and one fails, the response is HTTP 200 with data populated for the two that worked and an errors entry for the one that did not. Code that checks res.ok and nothing else will silently treat a missing result as a null:

js
const res = await fetch(endpoint, options);
const body = await res.json();

// Not enough — a 200 can carry errors.
if (!res.ok) throw new Error(`HTTP ${res.status}`);

if (body.errors?.length) {
  const retryable = body.errors.some((e) =>
    ['RATE_LIMITED', 'SERVICE_UNAVAILABLE', 'INTERNAL_SERVER_ERROR', 'NETWORK_ERROR']
      .includes(e.extensions?.code),
  );
  // body.data may still hold results for the APIs that succeeded.
  handlePartial(body.data, body.errors, retryable);
}

Decide deliberately whether a partial result is usable in your case. For a dashboard where each tile is independent, rendering two of three is better than failing the screen; for a calculation that needs all three inputs, it is not.

Practical notes

Ask for what you render. Requesting fields you discard costs bandwidth and nothing else — the credit is charged per API regardless of how many fields you name — but it also makes the query harder to read and hides which fields you actually depend on.

Draft in the explorer, ship from code. Autocomplete and schema validation catch a misspelled field before it becomes a GRAPHQL_VALIDATION_ERROR in production.

Cache the same way you would cache REST. The response is deterministic for a given query and input, and a cached response costs no credits. The cache-window guidance in making requests applies unchanged.

Watch your balance from a REST call. GraphQL responses do not carry the x-api-remaining-credits header, so if you need to monitor usage, read it from a REST call or from analytics rather than expecting it here.

Do not put a query in a GET. Besides being rejected with 405, it would put your input in a URL that ends up in logs.

Next

GraphQL overview has the limits, the cost model and what the gateway will not do. Errors covers the REST side of the same failures, and authentication covers the key that both use.

Was this page helpful?

Last updated