Transaction Enrichment API

Identify a merchant

Identify the merchant behind a merchant name or card descriptor. Every non-empty transaction returns one row: matched with a merchant domain, or cleaned with a display name and no domain.

Transaction is a private beta with its own host and its own tx_beta_ key. Errors use a single error string, not the Logo.dev API error object. Read the Transaction guide for access, the daily allowance, and how to act on a row. Descriptor matching is experimental.

post/transaction

Request

  • Base URL: https://www.logo.dev/api
  • URL: https://www.logo.dev/api/transaction
  • Auth: HTTP bearer

Request body

transactionstring required

The merchant name or card descriptor. The API trims surrounding whitespace, then the value must be 1 to 1,024 characters.

country_codestring

ISO 3166-1 alpha-2 country code, such as US or GB. Lowercase is accepted. The code is a hint for ambiguous names. It does not restrict results to that country. An unknown code returns 400. With transaction_context, the codes AQ, AX, TF, and VA return 400.

Example request

{
  "transaction": "Starbucks",
  "country_code": "US",
  "transaction_context": {
    "date": "2026-09-04",
    "amount": 10,
    "currency": "USD"
  }
}

Response

A matched row or a cleaned row. Every non-empty transaction returns one. The examples are illustrative: they show the shape, not the result for a particular string.

domainstring nullable required

The merchant's domain. null on a cleaned row.

namestring required

The display name. Present for every non-empty transaction.

  • When Brand resolves a brand for the domain, its name wins.
  • Otherwise it is the name the transaction lookup returned.
  • On a cleaned row it is the descriptor, tidied into a display name.

Descriptors arrive in one case, so a tidied name reads Fedex, not FedEx. When the Logo.dev brand corpus holds the same name, a cleaned row takes the company's own spelling. Only the name changes. The row stays cleaned at the same confidence.

entity_type'person' | 'organization' | 'null' nullable

Present only when the request includes transaction_context. person or organization, as the enrichment provider classified the other party, or null when it classified nobody.

A null does not mean the API found no merchant. A later stage can identify one, so a matched row can carry null here. A person always comes back as a cleaned row.

Without context, the API does not detect people. It treats a personal name as a merchant name, so Jane Doe can match an organization with a similar name.

entity_idstring nullable required

The merchant's Logo.dev brand id, the same id Brand uses. A brand keeps its id when its domain changes, so key your records on entity_id, not domain.

A row with an id has two sources that agree: the pipeline identified the merchant, and Brand resolves a brand for the same domain. Branch on entity_id, not on confidence.

null on a cleaned row, and on a matched row when Brand resolves no brand for the domain. A domain that Logo.dev has not indexed yet also returns null. A later lookup can return an id after the first crawl finishes.

logo_urlstring uri nullable required

The Logo.dev CDN URL for the merchant's logo. null on a cleaned row. The URL has no token. Add your publishable key before you show it: https://img.logo.dev/starbucks.com?token=LOGO_DEV_PUBLISHABLE_KEY. The same applies to logo_url in parent and intermediary.

status'matched' | 'cleaned' required

matched when the API identified a merchant domain. cleaned when it did not. There is no not-found result: a cleaned row is an answer, with a tidied display name and null in domain, entity_id, logo_url, and parent.

A row is cleaned for one of two reasons:

  • Nothing identified the descriptor.
  • A candidate domain failed a merchant check. The checks catch repeated hostnames, directory and profile hosts, a known platform in the merchant's place, and known descriptor conflicts. They run on saved results and on new lookups.

A failed check drops the domain and uses the tidied descriptor as the name. A platform that a check removes from the merchant's place still comes back in intermediary. An organization with no usable website also comes back cleaned. The API never puts a payment processor's or parent company's domain in place of the merchant's.

match_type'enriched_context' | 'descriptor_domain' | 'resolved_entity' | 'searched_name' | 'cleaned_descriptor' required

The pipeline stage that produced the row, strongest first. The set is closed.

  • enriched_context: identified from the full transaction, with entry type and account holder.
  • descriptor_domain: the descriptor printed the merchant's own domain.
  • resolved_entity: identified from the descriptor alone, with no transaction context.
  • searched_name: the Logo.dev brand corpus recognized the cleaned name.
  • cleaned_descriptor: nothing identified the merchant. name holds the tidied descriptor.

descriptor_domain and searched_name are fallbacks. They run only when the enrichment provider identifies nobody. A descriptor that the provider recognizes never reaches them: CHEWY.COM XXX-XXX-7111 FL prints a domain and still returns resolved_entity. Both fallbacks face the same merchant checks, so a row that fails them comes back cleaned.

Read match_type from the row. Do not predict it from the descriptor, because the same string can take a different path as the provider's coverage changes.

confidencenumber required

A score from 0 to 1. Two inputs set it: the match_type that produced the row, and whether Brand resolves a brand for the same domain. A printed domain scores above a name resolved from the descriptor, and a corpus search scores below it. A failed merchant check sends the row to the floor. A matched row with no brand has a lower score and a null entity_id.

The same input always gets the same score, so two runs of a batch compare cleanly.

Logo.dev publishes no cut-off, because the right line depends on what the row feeds. Use the score to rank and triage, for example to sort a review queue. To branch, read entity_id. Logo.dev can recalibrate the score without changing which rows have an id. A high score does not guarantee accuracy.

Example response

{
  "name": "Starbucks",
  "entity_id": "brand_5k2mq8h0000g00400000000017",
  "logo_url": "https://img.logo.dev/starbucks.com",
  "parent": {
    "domain": "meta.com",
    "name": "Meta",
    "entity_id": "brand_9r3hx5f0000g00400000000021",
    "logo_url": "https://img.logo.dev/meta.com"
  },
  "intermediary": {
    "domain": "squareup.com",
    "name": "Square",
    "entity_id": "brand_7c1pz4d0000g00400000000052",
    "logo_url": "https://img.logo.dev/squareup.com"
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.