API de Voos
API

Ofertas de Voos

Acessar ofertas destacadas e promocionais de voos

Ofertas de Voos

Acesse ofertas pré-selecionadas de voos com preços especiais. Ideal para exibir em home pages, landing pages e seções de destaques.

Ofertas Curadas

As ofertas são pré-selecionadas e otimizadas para conversão, com preços competitivos e rotas populares.


POST /api/ofertas/buscar

Busca ofertas de voos com filtros opcionais.

Request

{
  "internacional": true,
  "quantidade": 10,
  "shuffle": true,
  "page": 0,
  "pageSize": 20,
  "origem": "GRU",
  "destino": "LIS",
  "apenasMilhas": false
}

Parâmetros

ParâmetroTipoObrigatórioDescrição
internacionalbooleanNãoFiltrar por voos internacionais (padrão: false)
quantidadenumberNãoQuantidade de ofertas a retornar (padrão: 50, 0 = todas)
shufflebooleanNãoEmbaralhar resultados (padrão: true)
pagenumberNãoNúmero da página para paginação (padrão: 0)
pageSizenumberNãoTamanho da página (padrão: 0 = sem paginação)
origemstringNãoCódigo IATA do aeroporto de origem (ex: "GRU", "SDU", "JFK")
destinostringNãoCódigo IATA do aeroporto de destino (ex: "LIS", "MAD", "CDG")
apenasMilhasbooleanNãoSe true, retorna apenas ofertas com milhas (padrão: false)

Response

{
  "success": true,
  "data": [
    {
      "id": "oferta_123",
      "origem": {
        "iata": "GRU",
        "cidade": "São Paulo",
        "estado": "SP",
        "pais": "Brasil",
        "aeroporto": "Aeroporto Internacional de São Paulo/Guarulhos",
        "urlImagens": [
          "repository/places/são paulo - guarulhos/0"
        ]
      },
      "destino": {
        "iata": "MIA",
        "cidade": "Miami",
        "estado": "FL",
        "pais": "Estados Unidos",
        "aeroporto": "Miami International Airport",
        "urlImagens": [
          "repository/places/miami/2",
          "repository/places/miami/3"
        ]
      },
      "precoMinimo": 1299.00,
      "moeda": "BRL",
      "companhiaAerea": {
        "codigo": "LA",
        "nome": "LATAM Airlines"
      },
      "tipo": "round_trip",
      "internacional": true,
      "pontosAdulto": 1,
      "valorOriginalMilhas": 35000,
      "dataInicio": "2026-03-01",
      "dataFim": "2026-06-30",
      "restricoes": [
        "Antecedência mínima de 7 dias",
        "Permanência mínima de 3 dias"
      ],
      "disponivel": true
    }
  ],
  "totalItens": 150,
  "page": 0,
  "pageSize": 10,
  "message": "Ofertas encontradas com sucesso"
}

Campos da Resposta

CampoTipoDescrição
idstringID único da oferta
origemobjectInformações do aeroporto de origem (contém iata, cidade, estado, pais, urlImagens[])
destinoobjectInformações do aeroporto de destino (contém iata, cidade, estado, pais, urlImagens[])
precoMinimonumberMenor preço encontrado para esta rota
moedastringMoeda do preço (ex: BRL, USD)
companhiaAereaobjectCompanhia aérea principal (código e nome)
tipostringTipo de viagem: one_way ou round_trip
internacionalbooleanSe é um voo internacional
pontosAdultonumberIndica se aceita milhas: 1 = aceita milhas, 0 = apenas pagante
valorOriginalMilhasnumberValor em milhas necessário (disponível quando pontosAdulto > 0)
dataIniciostringData de início da validade da oferta
dataFimstringData de fim da validade da oferta
restricoesarrayLista de restrições da oferta
disponivelbooleanSe a oferta está disponível

Campos do Objeto Origem/Destino

CampoTipoDescrição
iatastringCódigo IATA do aeroporto (ex: "GRU", "MIA")
cidadestringNome da cidade
estadostringEstado/província (opcional)
paisstringNome do país
urlImagensstring[]Array de paths das imagens (pode conter 0 a 5 imagens, geralmente 1-3). Recomenda-se seleção aleatória para variedade visual.

Como Obter Imagens dos Destinos

As imagens são retornadas no campo urlImagens dentro dos objetos origem e destino. Para construir a URL completa da imagem:

Construção da URL da Imagem

A API retorna apenas o path da imagem. Você precisa construir a URL completa usando o CDN base:

CDN Base: https://mbxrepo.azureedge.net/prod

Formato do path retornado:

"repository/places/nome-da-cidade/0"
"repository/places/nome-da-cidade/1"
"repository/places/nome-da-cidade/2"

Nota: O array urlImagens pode conter 0 a 5 imagens (geralmente 1-3). Os números no final do path (0, 1, 2, etc.) indicam diferentes imagens do mesmo destino.

URL completa construída:

https://mbxrepo.azureedge.net/prod/repository/places/nome-da-cidade/0_298x160.webp

Exemplo de Implementação

const MOBLIX_CDN_BASE_URL = 'https://mbxrepo.azureedge.net/prod';
const IMAGE_SIZE = '_298x160.webp';

function buildImageUrl(imagePath) {
  if (!imagePath) return null;
  
  // Remove "repository/" se estiver no início
  let cleanPath = imagePath.replace(/^repository\//, '');
  
  // Converte para minúsculas (CDN é case-sensitive)
  cleanPath = cleanPath.toLowerCase();
  
  // Adiciona tamanho se não tiver extensão
  if (!cleanPath.includes('.webp') && !cleanPath.includes('.jpg')) {
    cleanPath = `${cleanPath}${IMAGE_SIZE}`;
  }
  
  // Encode para URL
  const pathParts = cleanPath.split('/');
  const encodedPath = pathParts.map(part => encodeURIComponent(part)).join('/');
  
  return `${MOBLIX_CDN_BASE_URL}/${encodedPath}`;
}

// Função para obter primeira imagem válida
function getFirstValidImage(imagePaths) {
  if (!imagePaths || !Array.isArray(imagePaths) || imagePaths.length === 0) {
    return null;
  }
  
  // Retorna a primeira imagem válida do array
  for (const path of imagePaths) {
    const url = buildImageUrl(path);
    if (url) return url;
  }
  
  return null;
}

// Função para obter imagem aleatória (recomendado para variedade visual)
// Usa seed para garantir que mesma oferta sempre mostre mesma imagem
// mas diferentes ofertas mostrem imagens diferentes
function getRandomValidImage(imagePaths, seed) {
  if (!imagePaths || !Array.isArray(imagePaths) || imagePaths.length === 0) {
    return null;
  }

  // Filtrar apenas imagens válidas
  const validImages = [];
  for (const path of imagePaths) {
    const url = buildImageUrl(path);
    if (url) validImages.push(url);
  }

  if (validImages.length === 0) return null;
  if (validImages.length === 1) return validImages[0];

  // Usar seed para seleção determinística mas variada
  let randomIndex;
  if (seed !== undefined) {
    // Gerar número pseudo-aleatório baseado no seed
    const seedStr = String(seed);
    let hash = 0;
    for (let i = 0; i < seedStr.length; i++) {
      const char = seedStr.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash; // Convert to 32bit integer
    }
    randomIndex = Math.abs(hash) % validImages.length;
  } else {
    randomIndex = Math.floor(Math.random() * validImages.length);
  }

  return validImages[randomIndex];
}

// Uso: Seleção aleatória com seed (recomendado)
// O seed garante que cards com mesmo origem/destino mostrem imagens diferentes
const seed = oferta.idGeral || oferta.id || `${oferta.origem.iata}-${oferta.destino.iata}`;
const imagemDestino = getRandomValidImage(oferta.destino.urlImagens, seed);
const imagemOrigem = getRandomValidImage(oferta.origem.urlImagens, seed);
const imagemPrincipal = imagemDestino || imagemOrigem;

// Alternativa: Usar primeira imagem (mais simples, menos variedade)
// const imagemDestino = getFirstValidImage(oferta.destino.urlImagens);
// const imagemOrigem = getFirstValidImage(oferta.origem.urlImagens);
// const imagemPrincipal = imagemDestino || imagemOrigem;

Prioridade de Imagens

  1. Destino primeiro: Use imagens de destino.urlImagens se disponível
  2. Origem como fallback: Use imagens de origem.urlImagens se destino não tiver imagem
  3. Seleção aleatória: Recomenda-se usar getRandomValidImage com seed para que cards com mesmo origem/destino mostrem imagens diferentes, criando variedade visual
  4. Placeholder: Se nenhuma imagem estiver disponível, exiba um placeholder ou ícone

Múltiplas Imagens

A API retorna urlImagens como um array que pode conter 0 a 5 imagens (geralmente 1-3). Cada imagem representa uma foto diferente do mesmo destino. Use seleção aleatória para garantir que diferentes ofertas com mesmo origem/destino exibam imagens variadas.

Exemplo no React

function OfertaCard({ oferta }) {
  // Obter primeira imagem válida (destino tem prioridade)
  const imagemDestino = getFirstValidImage(oferta.destino.urlImagens);
  const imagemOrigem = getFirstValidImage(oferta.origem.urlImagens);
  const imagemPrincipal = imagemDestino || imagemOrigem;

  return (
    <div className="oferta-card">
      {imagemPrincipal ? (
        <img 
          src={imagemPrincipal} 
          alt={`${oferta.destino.cidade}, ${oferta.destino.pais}`}
          className="w-full h-48 object-cover"
        />
      ) : (
        <div className="w-full h-48 bg-gray-200 flex items-center justify-center">
          <PlaneIcon />
        </div>
      )}
      {/* resto do card */}
    </div>
  );
}

Exemplos de Uso

Ofertas Internacionais

curl -X POST https://app.apidevoos.dev/api/ofertas/buscar \
  -H "Authorization: Bearer sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "internacional": true,
    "quantidade": 20,
    "shuffle": true
  }'

Ofertas Nacionais

curl -X POST https://app.apidevoos.dev/api/ofertas/buscar \
  -H "Authorization: Bearer sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "internacional": false,
    "quantidade": 15,
    "shuffle": false
  }'

Com Paginação

curl -X POST https://app.apidevoos.dev/api/ofertas/buscar \
  -H "Authorization: Bearer sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "internacional": true,
    "page": 1,
    "pageSize": 10
  }'

Com Filtros de Origem e Destino

curl -X POST https://app.apidevoos.dev/api/ofertas/buscar \
  -H "Authorization: Bearer sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "internacional": true,
    "origem": "GRU",
    "destino": "LIS",
    "quantidade": 20,
    "shuffle": true
  }'

Nota: Os filtros de origem e destino são aplicados diretamente na API, resultando em melhor performance ao filtrar grandes volumes de ofertas.

Apenas Ofertas com Milhas

curl -X POST https://app.apidevoos.dev/api/ofertas/buscar \
  -H "Authorization: Bearer sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "internacional": true,
    "apenasMilhas": true,
    "quantidade": 20,
    "shuffle": true
  }'

Nota: Quando apenasMilhas é true, a API retorna apenas ofertas que podem ser pagas com milhas (pontos de programas de fidelidade como Smiles, LATAM Pass, Tudo Azul).


Implementação em React

import { useState, useEffect } from 'react';

const MOBLIX_CDN_BASE_URL = 'https://mbxrepo.azureedge.net/prod';
const IMAGE_SIZE = '_298x160.webp';

// Função para construir URL completa da imagem
function buildImageUrl(imagePath: string | undefined | null): string | null {
  if (!imagePath) return null;
  
  try {
    let cleanPath = imagePath.replace(/^repository\//, '');
    cleanPath = cleanPath.toLowerCase();
    
    if (!cleanPath.includes('.webp') && !cleanPath.includes('.jpg')) {
      cleanPath = `${cleanPath}${IMAGE_SIZE}`;
    }
    
    const pathParts = cleanPath.split('/');
    const encodedPath = pathParts.map(part => encodeURIComponent(part)).join('/');
    const fullUrl = `${MOBLIX_CDN_BASE_URL}/${encodedPath}`;
    
    new URL(fullUrl);
    return fullUrl;
  } catch {
    return null;
  }
}

// Função para obter primeira imagem válida
function getFirstValidImage(imagePaths: string[] | undefined | null): string | null {
  if (!imagePaths || !Array.isArray(imagePaths) || imagePaths.length === 0) {
    return null;
  }
  
  for (const path of imagePaths) {
    const url = buildImageUrl(path);
    if (url) return url;
  }
  
  return null;
}

// Função para obter imagem aleatória (recomendado)
function getRandomValidImage(
  imagePaths: string[] | undefined | null,
  seed?: string | number
): string | null {
  if (!imagePaths || !Array.isArray(imagePaths) || imagePaths.length === 0) {
    return null;
  }

  const validImages: string[] = [];
  for (const path of imagePaths) {
    const url = buildImageUrl(path);
    if (url) validImages.push(url);
  }

  if (validImages.length === 0) return null;
  if (validImages.length === 1) return validImages[0];

  let randomIndex: number;
  if (seed !== undefined) {
    const seedStr = String(seed);
    let hash = 0;
    for (let i = 0; i < seedStr.length; i++) {
      const char = seedStr.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash;
    }
    randomIndex = Math.abs(hash) % validImages.length;
  } else {
    randomIndex = Math.floor(Math.random() * validImages.length);
  }

  return validImages[randomIndex];
}

interface Oferta {
  id: string;
  origem: {
    iata: string;
    cidade: string;
    pais: string;
    urlImagens?: string[];
  };
  destino: {
    iata: string;
    cidade: string;
    pais: string;
    urlImagens?: string[];
  };
  precoMinimo: number;
  moeda: string;
  companhiaAerea: {
    codigo: string;
    nome: string;
  };
}

function OfertasVoos() {
  const [ofertas, setOfertas] = useState<Oferta[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function carregarOfertas() {
      try {
        const response = await fetch('/api/ofertas/buscar', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.NEXT_PUBLIC_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            internacional: true,
            quantidade: 12,
            shuffle: true
          })
        });

        const data = await response.json();
        
        if (data.success) {
          setOfertas(data.data);
        }
      } catch (error) {
        console.error('Erro ao carregar ofertas:', error);
      } finally {
        setLoading(false);
      }
    }

    carregarOfertas();
  }, []);

  if (loading) {
    return <div>Carregando ofertas...</div>;
  }

  return (
    <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
      {ofertas.map((oferta) => {
        // Obter imagem aleatória (destino tem prioridade)
        // Usa seed para garantir variedade visual entre cards com mesmo origem/destino
        const seed = oferta.idGeral || oferta.id || `${oferta.origem.iata}-${oferta.destino.iata}`;
        const imagemDestino = getRandomValidImage(oferta.destino.urlImagens, seed);
        const imagemOrigem = getRandomValidImage(oferta.origem.urlImagens, seed);
        const imagemPrincipal = imagemDestino || imagemOrigem;
        
        return (
        <div key={oferta.id} className="border rounded-lg overflow-hidden shadow-lg">
          {imagemPrincipal ? (
            <img 
              src={imagemPrincipal} 
              alt={`${oferta.destino.cidade}, ${oferta.destino.pais}`}
              className="w-full h-48 object-cover"
            />
          ) : (
            <div className="w-full h-48 bg-gray-200 flex items-center justify-center">
              <Plane className="h-12 w-12 text-gray-400" />
            </div>
          )}
          
          <div className="p-4">
            <div className="text-sm text-gray-600">
              {oferta.origem.cidade} → {oferta.destino.cidade}
            </div>
            
            <div className="text-2xl font-bold mt-2">
              {oferta.moeda} {oferta.precoMinimo.toFixed(2)}
            </div>
            
            <div className="text-sm text-gray-500 mt-1">
              {oferta.companhiaAerea.nome}
            </div>
            
            <button className="mt-4 w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700">
              Ver Detalhes
            </button>
          </div>
        </div>
      ))}
    </div>
  );
}
import { useState, useEffect } from 'react';

const MOBLIX_CDN_BASE_URL = 'https://mbxrepo.azureedge.net/prod';
const IMAGE_SIZE = '_298x160.webp';

// Função para construir URL completa da imagem
function buildImageUrl(imagePath) {
  if (!imagePath) return null;
  
  try {
    let cleanPath = imagePath.replace(/^repository\//, '');
    cleanPath = cleanPath.toLowerCase();
    
    if (!cleanPath.includes('.webp') && !cleanPath.includes('.jpg')) {
      cleanPath = `${cleanPath}${IMAGE_SIZE}`;
    }
    
    const pathParts = cleanPath.split('/');
    const encodedPath = pathParts.map(part => encodeURIComponent(part)).join('/');
    const fullUrl = `${MOBLIX_CDN_BASE_URL}/${encodedPath}`;
    
    new URL(fullUrl);
    return fullUrl;
  } catch {
    return null;
  }
}

// Função para obter primeira imagem válida
function getFirstValidImage(imagePaths) {
  if (!imagePaths || !Array.isArray(imagePaths) || imagePaths.length === 0) {
    return null;
  }
  
  for (const path of imagePaths) {
    const url = buildImageUrl(path);
    if (url) return url;
  }
  
  return null;
}

// Função para obter imagem aleatória (recomendado)
function getRandomValidImage(imagePaths, seed) {
  if (!imagePaths || !Array.isArray(imagePaths) || imagePaths.length === 0) {
    return null;
  }

  const validImages = [];
  for (const path of imagePaths) {
    const url = buildImageUrl(path);
    if (url) validImages.push(url);
  }

  if (validImages.length === 0) return null;
  if (validImages.length === 1) return validImages[0];

  let randomIndex;
  if (seed !== undefined) {
    const seedStr = String(seed);
    let hash = 0;
    for (let i = 0; i < seedStr.length; i++) {
      const char = seedStr.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash;
    }
    randomIndex = Math.abs(hash) % validImages.length;
  } else {
    randomIndex = Math.floor(Math.random() * validImages.length);
  }

  return validImages[randomIndex];
}

function OfertasVoos() {
  const [ofertas, setOfertas] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function carregarOfertas() {
      try {
        const response = await fetch('/api/ofertas/buscar', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.NEXT_PUBLIC_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            internacional: true,
            quantidade: 12,
            shuffle: true
          })
        });

        const data = await response.json();
        
        if (data.success) {
          setOfertas(data.data);
        }
      } catch (error) {
        console.error('Erro ao carregar ofertas:', error);
      } finally {
        setLoading(false);
      }
    }

    carregarOfertas();
  }, []);

  if (loading) {
    return <div>Carregando ofertas...</div>;
  }

  return (
    <div className="ofertas-grid">
      {ofertas.map((oferta) => {
        // Seleção aleatória com seed para variedade visual
        const seed = oferta.idGeral || oferta.id || `${oferta.origem.iata}-${oferta.destino.iata}`;
        const imagemDestino = getRandomValidImage(oferta.destino.urlImagens, seed);
        const imagemOrigem = getRandomValidImage(oferta.origem.urlImagens, seed);
        const imagemPrincipal = imagemDestino || imagemOrigem;
        
        return (
        <div key={oferta.id} className="oferta-card">
          {imagemPrincipal && (
            <img src={imagemPrincipal} alt={`${oferta.destino.cidade}, ${oferta.destino.pais}`} />
          )}
          
          <div className="oferta-content">
            <div className="rota">
              {oferta.origem.cidade} → {oferta.destino.cidade}
            </div>
            
            <div className="preco">
              {oferta.moeda} {oferta.precoMinimo.toFixed(2)}
            </div>
            
            <div className="companhia">
              {oferta.companhiaAerea.nome}
            </div>
            
            <button>Ver Detalhes</button>
          </div>
        </div>
        );
      })}
    </div>
  );
}

Casos de Uso

1. Home Page - Destaques

Exiba ofertas em destaque na página inicial:

// Buscar 6 ofertas internacionais aleatórias
const response = await fetch('/api/ofertas/buscar', {
  method: 'POST',
  body: JSON.stringify({
    internacional: true,
    quantidade: 6,
    shuffle: true
  })
});

2. Landing Page - Destino Específico

Filtre ofertas por origem e destino diretamente na API:

// Buscar ofertas de São Paulo para Miami
const response = await fetch('/api/ofertas/buscar', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    internacional: true,
    origem: 'GRU',
    destino: 'MIA',
    quantidade: 10,
    shuffle: true
  })
});

const ofertas = await response.json();

Vantagem: Filtros aplicados no servidor resultam em menos dados transferidos e melhor performance.

3. Newsletter - Ofertas Semanais

Envie ofertas diferentes a cada semana:

// Sem shuffle para manter consistência
const response = await fetch('/api/ofertas/buscar', {
  method: 'POST',
  body: JSON.stringify({
    internacional: true,
    quantidade: 10,
    shuffle: false
  })
});

4. Comparador de Preços

Mostre as melhores ofertas por região:

const [nacionais, internacionais] = await Promise.all([
  fetch('/api/ofertas/buscar', {
    method: 'POST',
    body: JSON.stringify({ internacional: false, quantidade: 20 })
  }).then(r => r.json()),
  
  fetch('/api/ofertas/buscar', {
    method: 'POST',
    body: JSON.stringify({ internacional: true, quantidade: 20 })
  }).then(r => r.json())
]);

5. Buscar Apenas Ofertas com Milhas

Filtre ofertas que podem ser pagas com milhas:

// Buscar apenas ofertas com milhas
const response = await fetch('/api/ofertas/buscar', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    internacional: true,
    apenasMilhas: true,
    quantidade: 15,
    shuffle: true
  })
});

const data = await response.json();

// As ofertas retornadas terão pontosAdulto > 0
// indicando que aceitam pagamento com milhas

Integração com Busca de Voos

Quando um usuário clicar em uma oferta, redirecione para a busca de voos:

function handleOfertaClick(oferta) {
  // Calcular data de partida (ex: 30 dias à frente)
  const departureDate = new Date();
  departureDate.setDate(departureDate.getDate() + 30);
  
  // Calcular data de retorno (ex: 7 dias depois)
  const returnDate = new Date(departureDate);
  returnDate.setDate(returnDate.getDate() + 7);
  
  // Redirecionar para busca
  const searchParams = new URLSearchParams({
    origin: oferta.origem.iata,
    destination: oferta.destino.iata,
    departureDate: departureDate.toISOString().split('T')[0],
    returnDate: returnDate.toISOString().split('T')[0],
    passengers: '1',
    cabinClass: 'economy'
  });
  
  window.location.href = `/voos/buscar?${searchParams.toString()}`;
}

Dicas de UX

Melhores Práticas

  • Embaralhe ofertas para evitar repetição visual
  • Exiba imagens atraentes dos destinos usando seleção aleatória para variedade
  • Destaque o preço como elemento principal
  • Adicione CTAs claros como "Ver Voos" ou "Buscar"
  • Mostre restrições de forma clara mas não intrusiva
  • Use cache de 1-4 horas (ofertas mudam pouco)
  • Selecione imagens aleatoriamente do array urlImagens para que cards com mesmo origem/destino mostrem imagens diferentes

Layout Sugerido

┌───────────────────────────────────┐
│     [Imagem do Destino]           │
│                                   │
├───────────────────────────────────┤
│ São Paulo → Miami                 │
│                                   │
│ R$ 1.299,00                       │
│ LATAM Airlines                    │
│                                   │
│ [ Ver Voos ]                      │
└───────────────────────────────────┘

Cache e Performance

Recomendações de Cache

// Próximo.js - Cache de 2 horas
export const revalidate = 7200;

// Ou usar SWR
import useSWR from 'swr';

function OfertasVoos() {
  const { data, error } = useSWR(
    'ofertas',
    () => fetch('/api/ofertas/buscar', { 
      method: 'POST',
      body: JSON.stringify({ internacional: true, quantidade: 12 })
    }).then(r => r.json()),
    {
      refreshInterval: 7200000, // 2 horas
      revalidateOnFocus: false
    }
  );
}

Performance

  • Latência típica: 200-500ms
  • Cache recomendado: 1-4 horas
  • Refresh: A cada 2-4 horas para manter ofertas atualizadas

Erros Comuns

CódigoErroSolução
401UnauthorizedVerifique sua API key
400Invalid parametersVerifique tipos dos parâmetros
429Quota exceededReduza frequência ou adicione créditos
500Internal server errorTente novamente ou contate suporte

Próximos Passos

Após exibir ofertas, integre com:

On this page