Developers › Errors
Errors
Every error has the same shape and a stable machine-readable code. The message text may change; the code will not.
{
"error": {
"code": "quota_exceeded",
"message": "Quota exceeded.",
"status": 429,
"retry_after_seconds": 14878
}
}retry_after_seconds appears only on rate limits, and the same value is in the Retry-After header. It is in the body as well because many HTTP clients do not expose headers on the error path, and a client that cannot read the wait time ends up guessing.
Rejected requests are never billed. If you get any error, nothing was added to your invoice — including 429s and 400s in a retry loop.
Authentication and access
| Code | HTTP | What it means and what to do |
|---|---|---|
unauthorized | 401 | Missing or invalid API key. Send your key as `Authorization: Bearer syyni_live_…`. Keys are issued by Syyni; there is no self-service signup. |
key_paused | 403 | This API key is paused. The key exists but has been paused. Contact myynti@syynilabs.fi — pausing is reversible. |
key_revoked | 403 | This API key has been revoked. The key is permanently closed. Request a new one; revoked keys are never reactivated. |
customer_not_approved | 403 | Account is not approved for billing. Invoicing requires a credit decision before the first billable call. Your key works once that is in place. |
service_paused | 403 | The API is temporarily unavailable. Service-wide pause, not specific to your key. Retry later; status is announced at syynilabs.fi. |
spend_cap_reached | 403 | Monthly spend cap reached. A safety limit on your own account, not a payment problem. It clears at the start of the next month, or we can raise it. |
Request problems
| Code | HTTP | What it means and what to do |
|---|---|---|
unknown_parameter | 400 | Unknown query parameter. Unknown parameters are rejected, never ignored: a typo like `municipalty=091` would otherwise widen your query to the whole register — and bill you for it. See /developers/filters. |
invalid_parameter | 400 | Invalid parameter value. The parameter exists but the value has the wrong type or format. The message names the parameter. |
invalid_cursor | 400 | Invalid or stale cursor. Cursors are tied to the filters and fields they were issued for. If you change either, start a new page sequence. |
Limits
| Code | HTTP | What it means and what to do |
|---|---|---|
rate_limited | 429 | Too many requests. Per-minute burst limit. Wait the number of seconds in `Retry-After` and continue — nothing was billed. |
quota_exceeded | 429 | Quota exceeded. Daily request quota, monthly row quota or the account's monthly request cap reached. Quotas reset automatically at the time in `Retry-After`; contact us to raise them. |
Everything else
| Code | HTTP | What it means and what to do |
|---|---|---|
not_found | 404 | No such resource. The business ID does not exist in the dataset (or, with a sandbox key, is outside the sandbox set). Lookups by ID return ceased companies too; only list queries exclude them. |
internal_error | 500 | Something went wrong on our side. Nothing was billed. If it persists, send us the time and the request. |
Sandbox keys and errors
A sandbox key returns the same error codes as production, with two exceptions: customer_not_approved never applies, because nothing is billed, and spend_cap_reached cannot occur. Rate limits and quotas do apply — testing your retry logic is one of the things a sandbox is for.
Retrying
- 429 — wait
retry_after_secondsand repeat the identical request. This is the only error worth retrying automatically. - 5xx — retry once after a few seconds. If it persists, send us the timestamp and the request; nothing was billed.
- 400 and 403 — do not retry. The request or the account state has to change first, and a loop here will only consume your burst limit.
A note on repeat requests
If a request succeeds but your process crashes before storing the result, just run it again. Rows you have already paid for are free for 30 days, so recovering from a failure costs nothing. That is the reason the repeat window exists.