---
title: Production checklist
description: Keys, retries, polling, credits, and logging to set up before you run the Zaraftis v1 API in production.
sidebar:
  label: Production checklist
  order: 5
---

Go through this list before you send real traffic. Test one scan that succeeds, one request with a bad body, and one request with a key that lacks a scope, and check that your code handles each.

## Keep keys on the server

Store keys in a secret manager or an environment variable, never in browser or mobile code or in a repository. Give each integration its own key with only the scopes it needs, and limit it to the brands it works on. Revoke a key on the developer page as soon as it may have leaked.

## Send an Idempotency-Key with every POST that creates work

Make a new key for each new request and keep it with the request until you get a final answer. On a timeout or network error, retry with the same key so a scan is never started or charged twice. See [Safe retries](/conventions#safe-retries-with-idempotency-key).

## Retry only what can succeed

Retry when `error.retryable` is `true`: `429`, `500`, and `503`. Wait longer after each attempt (for example 1, 2, 4, then 8 seconds), add a random delay so your workers do not retry together, and stop after a few attempts. After `429`, wait at least the `Retry-After` seconds. Do not retry `4xx` errors without changing the request: fix the input, the key, or the credits first. See [Errors](/errors).

## Poll tasks calmly

Scans take minutes, not seconds. Poll `GET /v1/tasks/{task_id}` every few seconds at most, and stop at a terminal status. Handle `partial` as a finished scan with fewer results, not as an error.

## Check credits before large jobs

Read the price of each operation from `GET /v1/capabilities` and the balance from `GET /v1/billing/wallet`. Every paid request reserves its maximum cost first, so a large scan can fail with `402 insufficient_credits` even when the final cost would be lower. See [Credits and billing](/credits).

## Stay inside the limits

Size your worker pool to the rate limits and to `limits.concurrent_work` from `GET /v1/capabilities`. Read the `X-RateLimit-Remaining` header and slow down before it reaches zero. See [Rate limits](/conventions#rate-limits).

## Handle empty and partial data

A brand can have no competitors yet, a list can be empty, and a scan can be `partial`. Treat these as normal states in your product.

## Log enough to debug

For each request, log the method, path, status, `error.code`, and `request_id`. Do not log API keys. Quote the `request_id` when you report a problem.

## Follow changes

Read the [changelog](/changelog) before you upgrade, and ignore response fields your code does not know, so new fields do not break it.
