QannasAPI

Lide com resultados vazios e retentativas sem pagar duas vezes

O que você vai construir: um wrapper de chamada que distingue três desfechos — encontrado, não encontrado e falha — e só repete o terceiro, com idempotência para que uma retentativa nunca cobre em dobro.

SeçãoFundamentos da API
Ações usadasPOST /v1/data/contacts/find-email
Créditos5 (pior caso)
Funciona com chave de testeSim — roda em qk_test_ por zero créditos
Tempo estimado~10 min
Pré-requisitosUma chave e um cliente capaz de definir cabeçalhos de requisição.

São três desfechos, não dois

Um resultado encontrado é HTTP 200 com data preenchido e cobrança diferente de zero. Um resultado vazio é HTTP 200 com data: null e X-Credits-Charged: 0 — a busca rodou honestamente e não achou nada, então nada foi cobrado. Uma falha é 4xx ou 5xx. Só a terceira vale retentativa.

three outcomes
# FOUND — data populated, charged
# < 200  X-Credits-Charged: 5
{ "status": "OK", "data": { /* … */ } }

# MISS — ran honestly, found nothing, billed zero
# < 200  X-Credits-Charged: 0
{ "status": "OK", "data": null }

# FAILURE — nothing billed, safe to retry
# < 503

Não repita um resultado vazio

Um resultado vazio é uma resposta real sobre o mundo, não um erro transitório. Repetir não vai fazer surgir um resultado; só acrescenta latência. Guarde em cache por uma janela razoável e siga em frente.

branch.py
# Branch on the payload BEFORE the status code:
# a miss and a hit are both HTTP 200.
if res.status_code >= 500:
    return retry_later()    # transient

if res.json()["data"] is None:
    cache_miss(query, ttl=86_400)  # a real answer — do not retry
    return None

return res.json()["data"]

Repita falhas com idempotência

Os endpoints de dados aceitam o cabeçalho Idempotency-Key. Envie uma chave estável por operação lógica e uma retentativa após timeout se resolve no resultado original em vez de numa segunda chamada cobrada — o que mais importa quando você nunca chegou a ver a primeira resposta.

idempotent.sh
# A stable key per logical operation. If you never saw the first
# response, the retry resolves to it instead of billing twice.
curl -X POST https://api.qannasapi.com/v1/data/contacts/find-email \
  -H "Authorization: Bearer $QANNAS_API_KEY" \
  -H "Idempotency-Key: signup-8f21c4" \
  -H "Content-Type: application/json" \
  -d '{"query": "Jane Doe, Acme Logistics"}'

Recue e nunca repita um 4xx

Repita 5xx e timeouts com backoff exponencial e jitter. Um 422 significa que o corpo da requisição está errado: corrija em vez de reenviar. Falhas de origem nunca são cobradas, então repetir depois de uma falha real não custa nada a mais.

retry.py
import random, time

def call_with_retry(send, *, max_attempts=5):
    for attempt in range(max_attempts):
        res = send()

        if res.status_code == 422:
            raise ValueError(res.text)  # malformed — never resend

        if res.status_code < 500:
            return res

        # 5xx: upstream failures are never billed, so retrying is free
        time.sleep(0.5 * 2 ** attempt + random.uniform(0, 0.5))

    raise RuntimeError("exhausted retries")

Resumo

PadrãoComo
Encontrado200, data preenchido, X-Credits-Charged > 0
Vazio200, data: null, X-Credits-Charged: 0 — não repetir
Falha4xx / 5xx — repita apenas 5xx e timeouts
Retentativa seguraCabeçalho Idempotency-Key, estável por operação lógica
Requisição inválida422 — corrija o corpo, nunca reenvie como está
BackoffExponencial com jitter e número de tentativas limitado

Checklist de produção

  • Ramifique por data == null antes de ramificar pelo código de status.
  • Envie uma Idempotency-Key em tudo que você possa vir a repetir.
  • Repita 5xx e timeouts; nunca repita um 422.
  • Guarde resultados vazios em cache para não refazer a mesma pergunta o dia todo.

Próximos passos

Rode a estimativa primeiro. Ela não custa nada e aplica exatamente a mesma aritmética que o medidor.