Ir para o conteúdo

Busca por campos

Este guia documenta a superfície de busca por campos de GET /v1/brands (e seu gêmeo MCP search_brands): a lista completa de parâmetros, a semântica AND entre campos, a regra q + campos, a normalização de RUT, o novo campo matchedFields[] e a ordenação configurável com sort=. Todos esses parâmetros são aditivos e retrocompatíveis: uma chamada existente se comporta de forma idêntica, exceto pela mudança deliberada da ordem padrão sem-texto (veja Ordenação e sort=).

O schema completo e sempre atualizado por campo vive na referência OpenAPI interativa em /docs (Scalar). Este guia explica a semântica da busca combinada e enlaça a essa referência — não reformula o schema. Quando um campo não está documentado aqui, /docs é autoritativo.

Ao longo do guia a URL base é escrita como $TARNO_BASE_URL e a chave como $TARNO_API_KEY, e cada chamada leva o cabeçalho X-API-Key — igual ao guia de primeiros passos e ao guia de autenticação. O endpoint usa o scope brands:read.

O que este guia cobre

GET /v1/brands é uma única rota que serve dois modos livremente combináveis:

  • Busca de textoq (ou nombre) ranqueia pelos canais da denominação (exact / fuzzy / fonético / semântico, fundidos por RRF).
  • Filtragem por campos — predicados exatos, de intervalo e de pessoa (clase, estado, nroSolicitud, titular, rut, …) que restringem o conjunto de resultados.

Todos os parâmetros presentes se combinam com AND (veja Semântica AND). A resposta é uma página por keyset — a mesma forma { items: BrandSummary[], nextCursor } de primeiros passos.

Já tem uma lista de números de solicitação? Para obter o resumo de várias marcas conhecidas (uma carteira, uma watchlist) de uma vez, não busque: use POST /v1/brands/batch, que as resolve todas em uma chamada (veja Busca em lote).

Parâmetros

GET /v1/brands aceita estes parâmetros de consulta. Todos são opcionais. Um parâmetro multivalor é repetido na query (?clase=9&clase=25) e faz OR dentro do campo; campos distintos fazem AND entre si.

Param Tipo Multivalor Alias Notas
q string não Texto livre (FTS espanhol + fuzzy + fonético + semântico, RRF). É descartado apenas quando chega um campo competidor (nombre/denominacion/nroSolicitud/nroRegistro) → modo avançado; os filtros de restrição (clase, estado, tipoSigno, fechaPresentacion*, titular, representante, rut) mantêm q e apenas o restringem (veja regra q + campos).
nombre string não denominacion Texto sobre a denominação, com o mesmo motor que q.
denominacion string não nombre Alias de nombre.
nroSolicitud string não Número de solicitação, match exato.
nroRegistro string não Número de registro, match exato.
titular string sim Substring sobre o nome/razão social do titular (papel titular/ambas), insensível a acentos e maiúsculas.
representante string sim Substring sobre o nome do representante (papel representante/ambas), insensível a acentos e maiúsculas.
rut string sim RUT normalizado (veja RUT); match exato contra titular e representante.
vigencia enum não vigente Limita titular/representante/rut à titularidade atual (vigente), à que já não o é (historica) ou a ambas (todas). Sem um desses três filtros → 400.
clase integer sim Classe de Nice, 145.
estado string sim Código de estado (catálogo controlado).
tipoSigno string sim Código de tipo de sinal (catálogo controlado).
fechaPresentacionDesde YYYY-MM-DD não fechaDesde Limite inferior da data de apresentação (fecha_presentacion >=), inclusivo.
fechaPresentacionHasta YYYY-MM-DD não fechaHasta Limite superior da data de apresentação (fecha_presentacion <=), inclusivo.
sort enum não Ordenação dos resultados (veja Ordenação e sort=). relevance (=relevancia, padrão) · fechaPresentacion:desc/:asc (=recientes/antiguas) · fechaActualizacion:desc/:asc · denominacion:asc/:desc · estado/estado:asc/estado:desc.
limit integer não Tamanho da página. Padrão 20, máximo 100.
cursor string não Cursor de keyset opaco (veja paginação). Trate-o como uma caixa preta. Mutuamente exclusivo com offset.
offset integer não Paginação por offset (veja paginação). 010000. Só com um sort não de relevância; mutuamente exclusivo com cursor.

Um valor de sort fora do enum retorna 400 validation_error (o envelope de erro uniforme). Um rut sintaticamente inválido retorna 400 invalid_rut (veja RUT).

Titularidade atual, e como pedir a anterior

titular, representante e rut casam por omissão com quem o é hoje. Uma marca que mudou de mãos deixa de aparecer ao buscar pelo dono anterior — antes aparecia, e era uma resposta errada: devolvia uma carteira que essa pessoa já não tem.

vigencia muda a pergunta:

valor o que devolve
vigente (omissão) o que essa pessoa tem agora
historica o que teve e já não tem
todas ambas — o comportamento anterior, agora explícito
curl -sS "$TARNO_BASE_URL/v1/brands?titular=Kangol&vigencia=historica" -H "X-API-Key: $TARNO_API_KEY"

Limita apenas esses três filtros, por isso sem nenhum deles a resposta é 400: não filtraria nada, e quem pedisse historica e recebesse a omissão leria marcas atuais como se fossem o histórico.

Quem foi titular antes de uma marca concreta vive em personasHistoricas do detalhe — ver relatórios.

Semântica AND

Todos os parâmetros presentes se combinam com AND. Cada campo restringe mais o resultado:

# Marcas na classe 9 OU 25 (OR dentro de clase), E cujo titular contenha "sonda",
# E apresentadas a partir de 2020 (AND entre campos).
curl -sS "$TARNO_BASE_URL/v1/brands?clase=9&clase=25&titular=sonda&fechaPresentacionDesde=2020-01-01" \
  -H "X-API-Key: $TARNO_API_KEY"
  • OR dentro de um campo multivalor: ?clase=9&clase=25 = classe 9 ou 25.
  • AND entre campos distintos: adicionar titular= restringe aos que também cumprem esse predicado.

Não há OR entre campos nem um DSL com parênteses na v1 — o AND por campos cobre o caso motivador (advogados de PI restringindo por titular/classe/data). O conjunto vazio de parâmetros por campos não muda o comportamento em relação ao contrato base.

Regra q + campos (modo avançado)

q é descartado apenas quando chega junto com um campo competidor de texto/identidade — nombre/denominacion (alias), nroSolicitud ou nroRegistro—: então ativa-se o modo avançado e a busca combinada por campos assume. Os filtros de restrição (clase, estado, tipoSigno, fechaPresentacion*, titular, representante, rut) não descartam q: mantêm sua busca de texto e apenas a restringem com AND. Esta é a interpretação autoritativa do contrato (herdada da versão de busca combinada):

  • ?q=cafe sozinho ⇒ busca de texto livre clássica (ranqueia por relevância, leva score).
  • ?q=cafe&clase=43restrição (não é modo avançado): q é mantido; o resultado é "marcas Café na classe 43" — o texto ranqueia e clase o restringe —, ordenadas por relevância exceto se houver sort= explícito (veja Ordenação).
  • ?q=cafe&nombre=nescafemodo avançado: q é descartado (chegou um campo competidor); o texto de nombre= conduz a busca combinada por campos, ordenado pelo modo de ordenação vigente (veja Ordenação).
  • Para buscar por denominação dentro do modo avançado, use nombre= (não q=): nombre= é um campo competidor e sim participa do AND.

Em outras palavras: q é o modo simples de uma única caixa de texto; assim que você combina texto com um campo competidor (nombre/nroSolicitud/nroRegistro), mude para nombre para o texto. Restringir por clase/estado/datas/pessoas não requer mudança: q continua vigente e apenas é restringido.

Ordenação e sort=

O parâmetro sort= escolhe a ordem dos resultados. sort= explícito sempre prevalece sobre a ordem automática:

sort Ordem Paginação Notas
relevance (=relevancia, padrão) Por relevância (RRF) quando há texto; senão fecha_presentacion DESC keyset Com texto ranqueia pelos canais da denominação; sem texto cai para data DESC de forma silenciosa (não é erro).
fechaPresentacion:desc (=recientes) fecha_presentacion DESC, nro_solicitud ASC keyset ou offset Prevalece mesmo com texto: o texto continua filtrando, mas a ordem final é por data.
fechaPresentacion:asc (=antiguas) fecha_presentacion ASC, nro_solicitud ASC keyset ou offset Espelho determinístico.
fechaActualizacion:desc / :asc Pela última atualização do processo (updated_at) offset O campo updated_at é atualizado a cada re-scrape/upsert da marca.
denominacion:asc / :desc Alfabético pela denominação (insensível a acentos e maiúsculas) offset Ordem com acento/caixa normalizados (Ñ, acentos).
estado / estado:asc / estado:desc Agrupação pelo ciclo de vida do processo offset Não alfabético. Ordem: en trámite → observación de fondo → publicada → oposición → concedida → registrada → esperando renovación → rechazada → denegada → desistida → abandonada → anulada → caducado → vencida; estado desconhecido/nulo por último.

Um sort fora da lista devolve 400 validation_error. Os aliases relevancia/recientes/antiguas continuam válidos (equivalem a relevance/fechaPresentacion:desc/fechaPresentacion:asc).

Ordem padrão (sem sort=):

  • Com texto (q ou nombre) ⇒ relevância (RRF).
  • Sem textofecha_presentacion DESC, nro_solicitud ASC (mais recentes primeiro).

Mudança de comportamento. A listagem sem-texto (?clase=, ?estado= sozinhos) mudou sua ordem padrão de nro_solicitud ASC para fecha_presentacion DESC, nro_solicitud ASC (mais recentes primeiro). Se você precisa da ordem antiga por número de solicitação, hoje não há um valor de sort para isso; a ordem estável disponível é por data. Os cursores de keyset em trânsito de listagens sem-texto reiniciam uma vez após a mudança (um cursor= antigo de uma listagem sem-texto volta à página 1; basta reiniciar a paginação). Veja o changelog para a nota de versão.

Marcas com fecha_presentacion nula ficam ao final em ambos os modos (recientes e antiguas).

Teto de alcance com texto (F-4). Com texto (q/nombre/denominacion), recientes/antiguas reordenam por data o conjunto fundido limitado por relevância (≈600 marcas: os melhores candidatos de cada canal), não todo o corpus que casa. Ou seja, ?q=<termo comum>&sort=recientes devolve as mais novas entre os matches de maior relevância, não as mais novas de todos os matches. A listagem sem texto (sort= sem q) não tem esse teto: pagina o corpus completo por data sobre o índice de data. Para paginação por data sobre todo o corpus, liste sem texto (ou restrinja por clase/estado/fechaPresentacion*).

matchedFields[] — por que esta marca apareceu

Cada elemento de items[] pode incluir matchedFields[], um array aditivo que responde a "por que esta marca apareceu?". É opcional e omitido quando não há match de identidade/texto (por ex. uma listagem pura por ?clase=9, que apenas restringe).

Cada entrada é { field, kind }:

  • field — o campo de identidade/busca que deu match. Enum fechado: nombre, nroSolicitud, nroRegistro, titular, representante, rut. Os filtros de restrição (clase, estado, tipoSigno, fechaPresentacion*) nunca aparecem — são critérios de restrição, não de match.
  • kind — o canal pelo qual deu match. Enum: exact, fuzzy, phonetic, semantic.
  • nroSolicitud, nroRegistro, rut ⇒ sempre exact.
  • titular, representantefuzzy (substring insensível a acentos/maiúsculas).
  • nombre ⇒ o canal dominante quando deu match por vários ao mesmo tempo, segundo a hierarquia exact > fuzzy > phonetic > semantic. Uma única entrada por campo (nunca uma por canal).
{
  "nroSolicitud": "123456",
  "denominacion": "SONDA",
  "score": 0.91,
  "matchedFields": [
    { "field": "nombre", "kind": "exact" },
    { "field": "titular", "kind": "fuzzy" }
  ]
}

matchedFields[] é puramente aditivo: a forma existente de items[] não muda em nenhum outro aspecto. Um cliente que não o leia continua funcionando igual.

RUT: normalização, invalid_rut e cobertura

O parâmetro rut dá match exato contra persona.identificador de titular e representante (AND com o resto dos predicados). O servidor normaliza o valor antes de comparar, então aceitamos os três formatos habituais:

Entrada Normaliza para
76.123.456-7 76123456-7
761234567 76123456-7
76123456-7 76123456-7

O DV pode ser 09 ou K (maiúsculo).

Mudança de 2026-09-06: o rut de busca já NÃO exige que o módulo 11 feche. O corpus guarda 576 pessoas cujo identificador não passa no dígito verificador — arrastam 4.631 marcas, 686 como titular vigente — de modo que o único valor capaz de encontrá-las era exatamente o que a API rejeitava. O trabalho de um filtro é encontrar o que está armazenado; o DV é uma propriedade do dado guardado, não da consulta. Um rut bem formado cujo DV não calcula devolve agora um resultado vazio em vez de 400.

Erro invalid_rut. Um rut cuja forma não é a de um RUT (letras no corpo, ou um DV fora de 09/K) retorna 400 com { "error": { "code": "invalid_rut" } } — o envelope de erro uniforme. Nunca 500 nem um conjunto de resultados vazio silencioso. Esta validação e este código de erro são idênticos em REST e em MCP.

Cobertura parcial do RUT. Nem todas as marcas do corpus têm um RUT associado à sua pessoa. A título orientativo, cerca de ~43% das marcas têm RUT no titular e ~71% o têm no representante. Um rut válido que não está no corpus retorna um resultado vazio — isto está correto (cobertura parcial), não é um erro. Combine rut com titular/nombre quando quiser cobrir marcas sem RUT registrado.

Paginação: keyset e offset

dois modos de paginação. Escolha um; misturá-los (cursor e offset na mesma requisição) devolve 400 validation_error.

Keyset (por cursor) — o modo padrão

Estável e sem saltos, para sort=relevance (ou sem sort) e as ordens por data (fechaPresentacion:* / recientes / antiguas). Cada resposta leva um nextCursor:

  • Para a próxima página, reenvie nextCursor sem alterações como parâmetro cursor.
  • nextCursor: null significa fim dos resultados — pare.
  • O cursor é opaco (base64url das chaves de ordenação). Trate-o como uma caixa preta; sua forma interna depende do modo de ordenação vigente e pode mudar sem aviso.

sort=relevance (ou sem sort) pagina apenas por keyset: não aceita offset.

Offset (por deslocamento) — para pular para a página N

Para as ordens fechaActualizacion:*, denominacion:* e estado[:*] — e, se preferir, também as de data — use offset + limit:

  • offset é o número de resultados a pular (010000); limit é o tamanho da página.
  • No modo offset, nextCursor é null: para avançar, incremente offset (p. ex. página 3 com limit=20offset=40). Use estimatedTotal (presente em todos os modos) para saber quantas páginas há.
  • O teto de offset é 10000 (protege o banco de dados): um offset maior devolve 400 validation_error. Para ir mais longe, restrinja com filtros (clase/estado/datas/texto).

Regras (tudo que é inválido é 400 validation_error, nunca ignorado em silêncio):

Combinação Resultado
cursor e offset juntos 400 validation_error
offset com sort=relevance (ou sem sort e com texto) 400 validation_error (relevância é só keyset)
cursor com fechaActualizacion/denominacion/estado 400 validation_error (essas ordens são só offset)
offset > 10000 400 validation_error

Compatibilidade: as chamadas atuais (sem sort ou com relevancia/recientes/antiguas, e com cursor) continuam funcionando exatamente igual — o modo offset é puramente aditivo.

Veja primeiros passos para o padrão completo de paginação.

Busca em lote (batch)

Quando você já tem uma lista de números de solicitação — uma carteira, uma watchlist — e quer o resumo de todas de uma vez, use POST /v1/brands/batch em vez de N chamadas a GET /v1/brands/{nroSolicitud}. Ele resolve até 100 números de solicitação em uma chamada que consome uma única unidade de cota (não uma por marca). Mesmo scope brands:read.

Corpo (application/json): { "nros": [ … ] }.

curl -sS -X POST "$TARNO_BASE_URL/v1/brands/batch" \
  -H "X-API-Key: $TARNO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"nros":["1286842","823470","9999999"]}'

Resposta (200):

{
  "items": [
    { "nroSolicitud": "1286842", "denominacion": "…", "estado": "…", "clases": [9, 42], "titular": ["…"], "enrichmentPending": false },
    { "nroSolicitud": "823470",  "denominacion": "…", "estado": "…", "clases": [30],     "titular": [],    "enrichmentPending": false }
  ],
  "notFound": ["9999999"],
  "dataAsOf": "2026-07-20T03:17:00.000Z"
}
  • items — os BrandSummary encontrados, na mesma ordem em que você pediu os nros. É o mesmo resumo enriquecido (clases, titular, enrichmentPending) que a listagem de GET /v1/brands devolve. Como não há texto de busca, eles não levam score, matchedBy nem matchedFields.
  • notFound — os nros solicitados que não existem no corpus (na ordem de solicitação). Não é um erro: você pediu uma marca inexistente e nós dizemos isso explicitamente.
  • dataAsOf — o mesmo carimbo de frescor opcional do resto do contrato.

Regras:

  • nros repetidos colapsam em um (um número aparece uma vez, em items ou em notFound).
  • Devolve o resumo, não o detalhe. Para o detalhe completo de uma marca (classes + pessoas + anotações) use GET /v1/brands/{nroSolicitud}; o batch é para revisar muitas marcas de uma vez, não o processo de uma.
  • Limite de 100. Um batch com mais de 100 nros retorna 422 invalid_input. Um corpo com nros vazio ou malformado retorna 400 validation_error.
  • Uma unidade de cota por chamada, independentemente de quantos nros você pedir — esse é o ponto: evita o N+1 (e o gasto de N unidades de cota) de N chamadas individuais.

Paridade MCP

REST é equivalente a MCP: mesma chave, mesmos scopes, mesmos schemas Zod, mesma camada de consultas. A ferramenta MCP search_brands aceita exatamente os mesmos parâmetros por campos documentados aqui — incluindo sort, a normalização de rut e o erro invalid_rut — com idêntica semântica. A resposta (incluindo matchedFields[] e a ordenação) é byte-idêntica à do REST, verificado com um teste de paridade deep-equal contra Postgres real. A busca em lote tem seu próprio gêmeo, get_brands_batch, com a mesma forma { items, notFound, dataAsOf } e o mesmo limite de 100 (invalid_input).

Ferramenta MCP Equivalente REST
search_brands GET /v1/brands
get_brands_batch POST /v1/brands/batch

Veja o guia de MCP para o endpoint, o transporte e a autenticação.

Erros

Todos os erros usam o envelope uniforme { error: { code, message, requestId } } — descrito em primeiros passos.

HTTP code Quando
400 validation_error Parâmetros inválidos (por ex. sort fora do enum, clase fora de 145, data malformada, corpo de batch com nros vazio).
400 invalid_rut Um rut cuja forma não é a de um RUT (letras no corpo, DV fora de 09/K). Um DV que não calcula já não é erro: devolve vazio.
422 invalid_input Um batch (POST /v1/brands/batch) com mais de 100 nros (veja Busca em lote).
401 unauthorized Chave de API ausente, malformada, desconhecida ou revogada.
403 forbidden Falta o scope brands:read requerido.
429 rate_limited / quota_exceeded Limite de rajada ou cota mensal excedidos.

Veja também