API conventions
Errors, retries, idempotency and pagination for API integrations.
API environment
The API is served at https://api.spavik.co, which both SDKs use by default.
The request builder on each endpoint page calls the server declared in the
OpenAPI contract, with an API key sent in X-Spavik-Api-Key; see Authentication.
The engine and plan catalogues are public.
Each endpoint page includes request fields, response schemas and examples. Download the OpenAPI specification to import the contract into your own tools.
The error envelope
Every error has the same shape.
{
"status": "error",
"message": "CREDITS_EXHAUSTED",
"messageDetail": "Not enough credits."
}message carries an uppercase machine code, to read in your code.
messageDetail carries a readable sentence, to show or to log. Never branch
your logic on messageDetail: it is prose, it can change.
The full list is on the Error codes page.
The request_id
Every response carries an X-Spavik-Request-Id header. It is the only thing
worth quoting if you open a ticket: it locates the exact call in the logs.
Use the configured client and prediction inputs from Get started.
SDK exceptions expose the response identifier. For REST, save the headers
alongside the response body; MODEL_ID identifies your model and rows.json
contains the array of prediction rows. These examples make a billable call.
from spavik import SpavikError
try:
result = model.predict(rows)
print(result.request_id)
except SpavikError as error:
print(error.code, error.request_id)Idempotency
The engine mutations that support Idempotency-Key are POST /v1/models,
/predict, /outcomes, /refresh and POST /v1/forecast. Archive and
restore reach the same state however often they run, so they need no key.
A key is any string of up to 255 characters; a UUID is a good choice. A longer
key is refused with 400 IDEMPOTENCY_KEY_TOO_LONG.
A stored successful response is replayed without another computation or charge when you reuse the original endpoint, key and payload within the retention window (24 hours by default, starting with the original request). The key must be present on the original call. After a completed record expires, the same key can execute and charge again.
The same behavior applies to every data policy, including Zero Data Retention.
Successful responses are kept in a 24-hour technical cache for replay. Zero Data
Retention only disables the archiving of the inputs and outputs of evaluate,
backtest, forecast and predict; it does not change API bodies, parameters,
responses, outcome feedback or model refresh.
# The SDK creates a key and preserves it during this call's automatic retries.
result = model.predict(rows)
print(result.predictions)Use a unique key per logical call and preserve it across attempts. The SDKs do this for automatic retries; a fresh SDK call generates a new key.
IDEMPOTENCY_KEY_REUSED rejects different input under an existing key.
REQUEST_IN_PROGRESS means the original call is still active; honor
Retry-After. REQUEST_INTERRUPTED and IDEMPOTENCY_RESPONSE_UNAVAILABLE
mean the outcome is uncertain: stop automatic retries, keep X-Spavik-Request-Id
and inspect the request log or contact support. Do not force execution with
a new key. Unresolved records remain protected rather than expiring like
stored successes. See Costs and credits for the billing implications.
Pagination
Lists are walked with limit and offset, and return a pagination object.
{
"data": [],
"pagination": { "total": 0, "limit": 50, "offset": 0, "has_more": false }
}| Route | Default limit | Maximum |
|---|---|---|
GET /v1/models | 50 | 200 |
GET /v1/requests | 100 | 500 |
The libraries hide all of this: models() is an iterator that fetches pages as
it goes. Its limit only sets the page size; the iterator still walks every
page.
for model in spavik.models(limit=50):
print(model.id)Time windows
Routes that read history accept from and to as ISO 8601 dates, or days
for a rolling window.
| Parameter | Default | Maximum |
|---|---|---|
days | 30 | 365 |
GET /v1/requests additionally accepts status=error, which keeps only
failures: enough to track down a rejection that happened in production without
reproducing it.
Retries
Read the error code and the contract's x-retry-policies. Retry-After, when
present, supplies a delay; it does not by itself make a mutation safe to replay.
Retain the original Idempotency-Key and payload for retries.
For REQUEST_INTERRUPTED or IDEMPOTENCY_RESPONSE_UNAVAILABLE, the outcome is
uncertain. Do not switch to a new key or retry automatically. Inspect the request
log and contact support with X-Spavik-Request-Id. CREDITS_EXHAUSTED requires credits
or a plan change before retrying. See Error codes.
Rate limits
The /v1 engine routes apply no per-key request rate today. The
rate_limit_per_second value in GET /v1/plans is indicative and not enforced.
Plan quotas still apply: rows per prediction, context rows and active models.
If a 429 TOO_MANY_REQUESTS ever reaches you, wait for Retry-After and retry
with the same Idempotency-Key; the SDKs already do.
The SDKs provide bounded retries and preserve their idempotency keys within an operation. Starting a new SDK call creates a new logical operation; do not confuse it with replaying the original request.
Identifiers
They are opaque and prefixed. Do not parse them, do not rebuild them.
| Prefix | Object |
|---|---|
mdl_ | model |
ver_ | model version |
whk_ | webhook |
sk-spv-api- | API key |
Dates
Use ISO 8601 timestamps and normalize time zones consistently. Evaluation protocol timestamps are UTC; engine protocol v2 can omit the timezone suffix.
How is this guide?
Last updated on