Pular para o conteúdo

Boas práticas de consumo

Esta página reúne as melhores práticas para consumir a API do FazNota de forma estável, performática e que respeite os limites operacionais da plataforma.

1. Idempotência

Sempre envie numero-origem em emissões. Veja Idempotência.

{ "numero-origem": "PED-2026-00042", ... }

Sem ele, retries em caso de timeout podem gerar duplicatas.

2. Polling responsável

Para consultar recibo após emissão:

  • Intervalo mínimo: 2 segundos entre consultas do mesmo recibo.
  • Use backoff exponencial: 2s → 3s → 4.5s → 7s → 10s → 15s → 23s → 30s (cap).
  • Pare em status terminal (004, 010, 900, 999).
  • Timeout total razoável: 5 minutos. Após isso, acione o suporte.
async function polling(recibo, token) {
const TERM = ['004', '010', '900', '999'];
let wait = 2000;
const start = Date.now();
while (Date.now() - start < 5 * 60_000) {
const json = await mApi('GET', `/nfe/nota/${recibo}`, undefined, token);
if (TERM.includes(json.status)) return json;
await sleep(wait);
wait = Math.min(wait * 1.5, 30_000);
}
throw new Error('Timeout');
}

3. Cache local

Dados que não mudam com frequência podem ser cacheados:

RecursoTTL recomendado
Listagem de clientes1 hora
Listagem de produtos1 hora
Cliente individual (GET /clientes/{doc})1 hora
Produto individual (GET /produtos/{ref})1 hora
NFes emitidas (consulta por recibo)Sem cache durante emissão; cache longo após 004/010

Use Redis, Memcached ou cache em memória da aplicação.

4. Retry com backoff

Apenas em erros transitórios (HTTP 5xx, timeout, erro de rede). Não retente em erros de validação (status: "999" com mensagem clara):

async function comRetry(fn, maxTentativas = 3) {
for (let i = 1; i <= maxTentativas; i++) {
try {
return await fn();
} catch (err) {
// Se for ApiError de validação, propaga (não retentar)
if (err instanceof ApiError && err.payload?.status === '999') throw err;
if (i === maxTentativas) throw err;
await sleep(1000 * Math.pow(2, i - 1)); // 1s, 2s, 4s
}
}
}

5. Timeouts

Defina timeouts adequados no seu cliente HTTP:

OperaçãoTimeout recomendado
GET /health5 segundos
Listagens (GET /nfe, etc.)30 segundos
Consulta por recibo15 segundos
Emissão (POST)30 segundos
Cadastros (POST/PUT clientes/produtos)30 segundos

Timeout mínimo recomendado: 10 segundos. Menor que isso pode causar falsos timeouts.

6. Concorrência limitada

Evite enviar dezenas de requisições em paralelo no mesmo token:

// ❌ Ruim
const promises = clientes.map((c) => mApi('POST', '/clientes', c));
await Promise.all(promises);
// ✅ Bom — limite de 5 simultâneas
import pLimit from 'p-limit';
const limit = pLimit(5);
await Promise.all(clientes.map((c) => limit(() => mApi('POST', '/clientes', c))));

7. Headers de rate limit

Monitore os headers em cada resposta:

function checarRateLimit(res) {
const remaining = parseInt(res.headers.get('x-ratelimit-remaining') || '0', 10);
const reset = parseInt(res.headers.get('x-ratelimit-reset') || '0', 10);
if (remaining < 100) {
console.warn(`Rate limit baixo: ${remaining} restantes. Reset em ${new Date(reset * 1000)}`);
// Considere pausar emissões em massa
}
}

8. Sincronização incremental

Evite percorrer todo o histórico a cada sincronização. Use marcadores:

  • NFSe: filtro dataInicial.
  • Outros: salve o último id sincronizado e pare quando alcançá-lo.

Veja Sincronizar clientes e produtos para padrões completos.

9. Não polling sobre listas

Evite:

// ❌ Polling agressivo de listagens
setInterval(() => mApi('GET', '/nfe?page=1&page_size=100'), 5000);

Prefira:

  • Poll apenas o que precisa: o recibo específico, não a lista inteira.
  • Use cache local com TTL de minutos para listagens.

10. Tratamento correto de erros

  • HTTP 401/403/429: trate antes de qualquer lógica de domínio.
  • HTTP 200 + status: "999": erro de domínio. Veja data.erro.
  • HTTP 200 + status: "050": não é erro. É proteção; use o recibo retornado.
  • HTTP 200 + status: "900": rejeição SEFAZ; veja data.sefaz.mensagem.

Veja Tratamento de erros para detalhe.

11. Use X-Request-Id no suporte

Toda resposta traz meta.request_id e o header X-Request-Id com o mesmo valor. Cite esse valor sempre que abrir ticket. Sem ele, a investigação fica difícil.

12. Versionamento da API

Hoje a API está em v3 (nfemyse-v3 no path). Mudanças significativas de contrato serão anunciadas com antecedência via aviso prévio (ver Políticas operacionais).

Checklist de integração madura

  • Token armazenado em variável de ambiente ou cofre de segredos
  • Cliente HTTP centralizado com headers padrão e timeouts
  • numero-origem único em todas as emissões
  • Polling com intervalo mínimo de 2s e backoff exponencial
  • Tratamento de status: "999", "900", "050"
  • Cache local de clientes e produtos (TTL 1h)
  • Concorrência limitada (≤5 simultâneas no mesmo token)
  • Retry só em erros transitórios, com backoff
  • Monitoramento de X-RateLimit-Remaining
  • Log estruturado com meta.request_id em cada operação
  • Health check externo apontando para GET /health